Search Unity

Alpha Test then Color Set Shader for 2D Fluids

Discussion in 'Shaders' started by jfjordanfarr, Jul 2, 2015.

  1. jfjordanfarr

    jfjordanfarr

    Joined:
    Jul 2, 2015
    Posts:
    7
    Hi there!

    Right now I'm trying to write a shader (or shaders) which can perform two tasks:

    1. Run an alpha test on a render texture input (or raw camera input).
    2. Set all pixels which were not cut in the alpha test to a precise, uniform color and alpha, then outputs as an unlit texture.

    I'd like this shader to run on a plane which follows the main camera around OR on a camera itself. The finished output will be visible in front of most of the playable area, meaning that seeing through the plane is critical (a final alpha of 1 on the output texture is not an option - our player and the environment will be completely covered up). This output is a representation of water that our players can drown in.

    I've tried a plethora of different example shaders (including the vegetation shader from older Unity docs http://wiki.unity3d.com/index.php/VegetationTwoPass), and my trouble implementing this may just be my ineptitude with writing shaders (I'm brand new to it).

    Would anyone be willing to help me write such a shader? Or does anyone know an existing shader which already accomplishes these tasks? Thanks so much!
     
    Last edited: Jul 2, 2015
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Vertex-fragment shaders will give you better results here than surface shaders, but either way...

    Use this general logic wherever you're sampling your texture.
    Code (CSharp):
    1. float4 color = tex2D(_texture, uv);
    2.  
    3. if (color.a < 0.1) discard;
    4.  
    5. color = _Color;
    You will need a texture and color property in your shader. The discard keyword will cancel drawing the current pixel, so if you get past that statement, you know it's a pixel you want to keep so you can set it's color accordingly. You will need to disable unity's built-in alpha test if you've enabled it for the shader (though in most cases use theirs, it's more hardware accelerated).
     
  3. jfjordanfarr

    jfjordanfarr

    Joined:
    Jul 2, 2015
    Posts:
    7
    Thanks for the reply!

    I gave this shader code a shot, and the result was so slow that it crashed Unity on my powerful desktop:

    Code (CSharp):
    1. Shader "Custom/JordanAlphaTest" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (.5, .5, .5, .5)
    4.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    5.         _Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
    6.     }
    7.  
    8.  
    9. SubShader {
    10.     Tags { "RenderType"="TransparentCutout" }
    11.     LOD 200
    12.     Cull Off Lighting Off
    13.  
    14. CGPROGRAM
    15. #pragma surface surf Lambert
    16.  
    17. sampler2D _MainTex;
    18. fixed4 _Color;
    19. half _Cutoff;
    20.  
    21. struct Input {
    22.     float2 uv_MainTex;
    23. };
    24.  
    25. void surf (Input IN, inout SurfaceOutput o) {
    26.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
    27.     if (c.a < _Cutoff) discard;
    28.     o.Albedo = _Color.rgb;
    29.     o.Alpha = _Color.a;
    30. }
    31.  
    32. ENDCG
    33.  
    34. }
    35.  
    36. }
    What am I doing wrong here? _MainTex is correctly set as a 256x256 render texture, and the material containing this shader is applied to a stretched plane.
     
  4. karp505

    karp505

    Joined:
    Jul 24, 2014
    Posts:
    18
    Discard is very slow in general, and even more so in a surface shader. Try something like this:
    Code (CSharp):
    1. Shader "Custom/JordanAlphaTest" {
    2.     Properties {
    3.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    4.         _Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
    5.     }
    6. SubShader {
    7.     Tags { "RenderType"="TransparentCutout" "Queue" = "Transparent" }
    8.  
    9. Pass{
    10.  
    11. ZWrite Off
    12. Blend SrcAlpha OneMinusSrcAlpha
    13. CGPROGRAM
    14.  
    15. #pragma vertex vert
    16. #pragma fragment frag
    17.  
    18. sampler2D _MainTex;
    19. half _Cutoff;
    20.  
    21. struct vertIn{
    22.     float4 vertex : SV_POSITION;
    23.     half2 texcoord : TEXCOORD0;
    24. };
    25.  
    26. struct vertOut{
    27.     float4 vertex : SV_POSITION;
    28.     half2 texcoord : TEXCOORD0;
    29. };
    30. vertOut vert(vertIn i)
    31.          {
    32.             vertOut o;
    33.             o.vertex = mul (UNITY_MATRIX_MVP, i.vertex);
    34.             o.texcoord = i.texcoord;
    35.             return o;
    36.          }
    37.          fixed4 frag(vertOut i) : COLOR
    38.          {
    39.             fixed4 c = tex2D(_MainTex, i.texcoord);
    40.             c.a *= step(_Cutoff, c.a);
    41.             return c;
    42.          }
    43. ENDCG
    44. }
    45. }
    46. }
    Warning: this is untested code. Let me know if you run into trouble.
     
  5. jfjordanfarr

    jfjordanfarr

    Joined:
    Jul 2, 2015
    Posts:
    7
    Step was a smart call there, Karp505, and that shader was very close! Unfortunately my solution required a hard set on alpha, which means actual alpha testing. I ended up with a shader which uses clip() which I received help with from the SomethingAwful goons.

    Here's the final shader code:
    Code (CSharp):
    1. Shader "Custom/JordanAlphaTest" {
    2.     Properties {
    3.     _Color ("Main Color", Color) = (.5, .5, .5, .5)
    4.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    5.         _Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
    6.     }
    7.  
    8.  
    9.     SubShader {
    10.  
    11.     Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
    12.    
    13.      Blend SrcAlpha OneMinusSrcAlpha
    14.    
    15.     Pass
    16.     {
    17.         CGPROGRAM
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.          
    21.             #include "UnityCG.cginc"
    22.          
    23.             sampler2D _MainTex;
    24.             fixed _Cutoff;
    25.             fixed4 _Color;
    26.          
    27.             struct v2f
    28.             {
    29.                 float4  pos : SV_POSITION;
    30.                 float2  uv : TEXCOORD0;
    31.             };
    32.  
    33.             v2f vert (appdata_base v)
    34.             {
    35.                 v2f o;
    36.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    37.                 o.uv = v.texcoord.xy;
    38.                 return o;
    39.             }
    40.  
    41.             float4 frag(v2f i) : COLOR
    42.             {
    43.                 float4 c = tex2D(_MainTex, i.uv);
    44.                 clip(c.a - _Cutoff);
    45.                 c.rgb = _Color.rgb;
    46.                 c.a = _Color.a;
    47.                 return c;
    48.             }
    49.         ENDCG
    50.     }
    51.  
    52. }
    53. }
    The _MainTex input is a rendertexture from a camera that only views our 2D fluids, and in order to get this to not render in strange ways, I had to include some code on the quad carrying this shader to give it a 2D sorting layer and order in that layer (see http://forum.unity3d.com/threads/using-non-sprites-with-the-new-sorting-layers.211822/). The tags also turned out to be very important.

    The last problem I have is getting that quad to resize correctly to follow the main camera around, and adjust tiling settings on the texture to account for any squashing or stretching incurred by having a power-of-two render texture.

    Edit: For anyone interested in using gradient balls for fluid particles with an alpha cutoff, a blur effect on the camera viewing them dramatically improves the effect.
     
    Last edited: Jul 7, 2015