Search Unity

Achieving a multi pass effect with a Surface Shader

Discussion in 'Shaders' started by Sycle, Jul 12, 2011.

  1. Sycle

    Sycle

    Joined:
    Nov 24, 2009
    Posts:
    446
    Heya! I've been scratching my head over this and was hoping someone might be able to point me in the right direction.

    I'm trying to add a silhouette effect to some characters so they're visible through objects, this is a mockup of what I'm going for :



    Normally I'd do this with a flat shaded second pass which uses "ztest Greater" but I'm using a Surface Shader which don't seem compatible with additional passes.

    Is there a way to do this or get an equivalent result while still using a Surface Shader?

    Code (csharp):
    1. Shader "Character Indicator" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1,1,1,1)
    4.     _MainTex ("Base (RGB)", 2D) = "white" {}
    5.     _WrapTex ("Wrap ramp (RGBA)", 2D) = "black" {}
    6.     _IndicatorTex ("Indicator Lights (RGB)", 2D) = "white" {}
    7.     _Indicator ("Indicator Color", Color) = (1,1,1,1)
    8.     _Cutoff ("Alpha cutoff", Range (0,1)) = 0.0
    9. }
    10.  
    11. SubShader {
    12.     Tags { "RenderType" = "Opaque" }
    13.  
    14. CGPROGRAM
    15. #pragma surface surf Ramp alphatest:_Cutoff
    16.  
    17. uniform float4 _Color;
    18. uniform float4 _Indicator;
    19. uniform sampler2D _MainTex;
    20. uniform sampler2D _WrapTex;
    21. uniform sampler2D _IndicatorTex;
    22.  
    23. half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
    24.     half NdotL = dot (s.Normal, lightDir);
    25.     half diff = NdotL * 0.5 + 0.5;
    26.     half3 ramp = tex2D (_WrapTex, float2(diff)).rgb;
    27.     half4 c;
    28.     c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
    29.     c.a = s.Alpha;
    30.     return c;
    31. }
    32.  
    33. struct Input {
    34.     float2 uv_MainTex;
    35.     float2 uv_BumpMap;
    36.     float3 viewDir;
    37. };
    38.  
    39. void surf (Input IN, inout SurfaceOutput o) {
    40.     o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Color;
    41.     o.Emission = tex2D ( _IndicatorTex, IN.uv_MainTex).rgb * _Indicator * 2;
    42.    
    43.     half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
    44.     o.Emission += tex2D ( _MainTex, IN.uv_MainTex).rgb * 2 * pow (rim, 3.0);
    45.     o.Alpha = tex2D ( _MainTex, IN.uv_MainTex).a;
    46. }
    47.  
    48. ENDCG
    49.  
    50. }
    51.  
    52. Fallback " Glossy", 0
    53.  
    54. }
    Any help would be much appreciated!
     
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    You can't add arbitrary passes with surface shaders, but what you can do is use #pragma debug in your surface shader to get the generated passes in comments when you open the compiled shader. You can use this as a starting point and then add your extra passes by hand.
     
  3. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    You can add arbitrary passes while using a surface shader.

    You just stick everything else in Pass {} tags and ensure that your surface shader isn't inside any of them (as Unity will generate them as it parses the surface shader).

    For my river shader, I've successfully done a depth-based pass then a grab pass then a distortion pass which then has a surface shader rendering on top.

    Example code;
    Code (csharp):
    1.  
    2. Shader "Exploration/River" {
    3.     Properties {
    4.         _Color ("Main Color", Color) = (1,1,1,1)
    5.         _DepthColor ("Depth Color", Color) = (1,1,1,1)
    6.         _WaterDepth ("Water Depth", Range (0, 10)) = 1
    7.         _BumpMap ("Normal Shading (Normal)", 2D) = "bump" {}
    8.         _WaterSpeed ("Water Speed", Range (0, 10)) = 1
    9.         _WaterSpeed2 ("Water Speed", Range (0, 10)) = 0.37
    10.         _Fresnel ("Fresnel Value", Float) = 0.028
    11.     }
    12.     SubShader{
    13.         Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
    14.         Blend One One
    15.  
    16.         Pass
    17.         {
    18.             Name "RiverDepth"
    19.             Blend SrcAlpha OneMinusSrcAlpha
    20.             CGPROGRAM
    21.                 #pragma vertex vert
    22.                 #pragma fragment frag
    23.                 #pragma fragmentoption ARB_precision_hint_fastest
    24.                 #include "UnityCG.cginc"
    25.  
    26.                 struct v2f {
    27.                     float4 pos          : POSITION;
    28.                     float4 screenPos    : TEXCOORD0;
    29.                 };
    30.  
    31.                 v2f vert (appdata_full v)
    32.                 {
    33.                     v2f o;
    34.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);   
    35.                     o.screenPos = ComputeScreenPos(o.pos);
    36.                     return o;
    37.                 }
    38.  
    39.                 sampler2D _CameraDepthTexture;
    40.                 float4 _DepthColor;
    41.                 float _WaterDepth;
    42.  
    43.                 half4 frag( v2f i ) : COLOR
    44.                 {
    45.                     float depth = 1 - saturate(_WaterDepth - (LinearEyeDepth(tex2D(_CameraDepthTexture, i.screenPos.xy / i.screenPos.w).r) - i.screenPos.z));
    46.                     return half4(_DepthColor.rgb, depth * _DepthColor.a);
    47.                 }
    48.             ENDCG          
    49.         }
    50.  
    51.         GrabPass {
    52.             Name "RiverGrab"
    53.         }
    54.  
    55.         Pass
    56.         {
    57.             Name "RiverDistortion"
    58.             Blend Off
    59.             CGPROGRAM
    60.                 #pragma vertex vert
    61.                 #pragma fragment frag
    62.                 #pragma fragmentoption ARB_precision_hint_fastest
    63.                 #include "UnityCG.cginc"
    64.  
    65.                 struct v2f {
    66.                     float4 pos          : POSITION;
    67.                     float4 uvgrab       : TEXCOORD0;
    68.                     float2 uv           : TEXCOORD1;
    69.                     float4 screenPos    : TEXCOORD2;
    70.                 };
    71.  
    72.                 v2f vert (appdata_full v)
    73.                 {
    74.                     v2f o;
    75.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);   
    76.                     #if UNITY_UV_STARTS_AT_TOP
    77.                     float scale = -1.0;
    78.                     #else
    79.                     float scale = 1.0;
    80.                     #endif
    81.                     o.uvgrab.xy = (float2(o.pos.x, o.pos.y * scale) + o.pos.w) * 0.5;
    82.                     o.uvgrab.zw = o.pos.zw;
    83.                     o.uv = v.texcoord.xy;
    84.                     return o;
    85.                 }
    86.  
    87.                 sampler2D _BumpMap;
    88.                 float _WaterSpeed, _WaterSpeed2;
    89.                 sampler2D _GrabTexture;
    90.                 float4 _GrabTexture_TexelSize;
    91.  
    92.                 half4 frag( v2f i ) : COLOR
    93.                 {
    94.                     float2 riverUVs = i.uv;
    95.                     riverUVs.y += _Time * _WaterSpeed;
    96.                     float3 normal1 = UnpackNormal(tex2D(_BumpMap, riverUVs));
    97.                     riverUVs = i.uv;
    98.                     riverUVs.x *= -1;
    99.                     riverUVs.y += 0.3 + _Time * _WaterSpeed2;
    100.                     float3 normal2 = UnpackNormal(tex2D(_BumpMap, riverUVs));
    101.                     normal2 *= float3(1, 1, 0.5);
    102.                    
    103.                     float3 combinedNormal = normalize(normal1 * normal2);
    104.                
    105.                     float2 offset = combinedNormal.xy * 5 * _GrabTexture_TexelSize.xy;
    106.                     i.uvgrab.xy = (offset * i.uvgrab.z) + i.uvgrab.xy;
    107.                     return half4(tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab)).rgb, 1);
    108.                 }
    109.             ENDCG
    110.         }
    111.  
    112.         CGPROGRAM
    113.             #include "ExplorationLighting.cginc"
    114.             #pragma surface surf ExplorationRiver noambient novertexlights nolightmap
    115.             #pragma target 3.0
    116.            
    117.             struct Input
    118.             {
    119.                 float2 uv_BumpMap;
    120.             };
    121.  
    122.             sampler2D _BumpMap, _CameraDepthTexture;
    123.             float _Specular, _Gloss, _WaterSpeed, _WaterSpeed2;
    124.  
    125.             void surf (Input IN, inout SurfaceOutput o)
    126.             {
    127.                 float2 riverUVs = IN.uv_BumpMap;
    128.                 riverUVs.y += _Time * _WaterSpeed;
    129.                 float3 normal1 = UnpackNormal(tex2D(_BumpMap, riverUVs));
    130.                 riverUVs = IN.uv_BumpMap;
    131.                 riverUVs.x *= -1;
    132.                 riverUVs.y += 0.3 + _Time * _WaterSpeed2;
    133.                 float3 normal2 = UnpackNormal(tex2D(_BumpMap, riverUVs));
    134.                 normal2 *= float3(1, 1, 0.5);
    135.                
    136.                 float3 combinedNormal = normalize(normal1 * normal2);
    137.                
    138.                 o.Albedo = fixed3(1);
    139.                 o.Normal = combinedNormal;
    140.                 o.Alpha = 0;
    141.             }
    142.         ENDCG
    143.     }
    144.    
    145.     Fallback "Transparent/VertexLit"
    146. }
    147.  
    You can even use multiple surface shaders on top of one another and change the blending/zwrite/blah values of them;

    Code (csharp):
    1.  
    2. /*
    3. Alpha tested pass.
    4.  
    5. Alpha blended pass w/ ZWrite off and alphatest greater than _Cutoff.
    6.  
    7. Anisotropic highlight.
    8. */
    9.  
    10. Shader "Exploration/Hair Soft Edge Surface" {
    11.     Properties {
    12.         _Color ("Main Color", Color) = (1,1,1,1)
    13.         _MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
    14.         _SpecularTex ("Specular (R) Gloss (G) Null (B)", 2D) = "gray" {}
    15.         _BumpMap ("Normal (Normal)", 2D) = "bump" {}
    16.         _AnisoTex ("Anisotropic Direction (RGB)", 2D) = "bump" {}
    17.         _AnisoOffset ("Anisotropic Highlight Offset", Range(-0.5,0.5)) = -0.2
    18.         _Cutoff ("Alpha Cut-Off Threshold", Range(0,1)) = 0.5
    19.         _Fresnel ("Fresnel Value", Float) = 0.028
    20.     }
    21.  
    22.     SubShader {
    23.         Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" }
    24.  
    25.         CGPROGRAM
    26.             #include "ExplorationLighting.cginc"
    27.             #pragma surface surf ExplorationSoftHairFirst fullforwardshadows exclude_path:prepass
    28.             #pragma target 3.0
    29.            
    30.             struct Input
    31.             {
    32.                 float2 uv_MainTex;
    33.             };
    34.            
    35.             sampler2D _MainTex, _SpecularTex, _BumpMap, _AnisoDirection;
    36.                
    37.             void surf (Input IN, inout SurfaceOutputCharacter o)
    38.             {
    39.                 fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
    40.                 o.Albedo = albedo.rgb;
    41.                 o.Alpha = albedo.a;
    42.                 o.AnisoDir = tex2D(_AnisoDirection, IN.uv_MainTex);
    43.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    44.                 o.Specular = tex2D(_SpecularTex, IN.uv_MainTex).rgb;
    45.             }
    46.         ENDCG
    47.  
    48.         Blend SrcAlpha OneMinusSrcAlpha
    49.         ZWrite Off
    50.  
    51.         CGPROGRAM
    52.             #include "ExplorationLighting.cginc"
    53.             #pragma surface surf ExplorationSoftHairSecond fullforwardshadows exclude_path:prepass noforwardadd
    54.             #pragma target 3.0
    55.            
    56.             struct Input
    57.             {
    58.                 float2 uv_MainTex;
    59.             };
    60.            
    61.             sampler2D _MainTex, _SpecularTex, _BumpMap, _AnisoDirection;
    62.                
    63.             void surf (Input IN, inout SurfaceOutputCharacter o)
    64.             {
    65.                 fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
    66.                 o.Albedo = albedo.rgb;
    67.                 o.Alpha = albedo.a;
    68.                 o.AnisoDir = tex2D(_AnisoDirection, IN.uv_MainTex);
    69.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    70.                 o.Specular = tex2D(_SpecularTex, IN.uv_MainTex).rgb;
    71.             }
    72.         ENDCG
    73.     }
    74.     FallBack "Transparent/Cutout/VertexLit"
    75. }
    76.  
     
    Last edited: Jul 12, 2011
    TMPxyz, ZYM35, Raseru and 12 others like this.
  4. increpare

    increpare

    Joined:
    Nov 17, 2010
    Posts:
    151
    Ooh, count me interested in the outcome to this one : )
     
  5. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Count me in too, im interested in this one.

    Edit :
    Wait, how do you transfer the generated texture from the previous pass?
     
    Last edited: Jul 12, 2011
  6. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Very cool. I had no idea.
     
  7. Chickenlord

    Chickenlord

    Joined:
    May 13, 2011
    Posts:
    381
    You could also write just two surface shaders and Unity will generate the right thing from it. What i mean is:
    Code (csharp):
    1.  
    2. //1st pass
    3. CGPROGRAM
    4. #pragma surface surf
    5. ...
    6. ENDCG
    7.  
    8. //2nd pass
    9. CGPROGRAM
    10. #pragma surface surf
    11. ...
    12. ENDCG
    13.  
    Hope it helps ;)
     
    Shaelle and macrod like this.
  8. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Ehr, you don't... it just draws on top.
     
  9. HolBol

    HolBol

    Joined:
    Feb 9, 2010
    Posts:
    2,887
    Wow, this could be so useful! I am watching intently.
     
  10. Sycle

    Sycle

    Joined:
    Nov 24, 2009
    Posts:
    446
    Thankyou Farfarer! That was just what I needed to know!

    I got my shader working the way I wanted it to, for anyone curious it ended up looking like this :

    Code (csharp):
    1. Shader "Character Indicator" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1,1,1,1)
    4.     _MainTex ("Base (RGB)", 2D) = "white" {}
    5.     _WrapTex ("Wrap ramp (RGBA)", 2D) = "black" {}
    6.     _IndicatorTex ("Indicator Lights (RGB)", 2D) = "white" {}
    7.     _Indicator ("Indicator Color", Color) = (1,1,1,1)
    8.     _Cutoff ("Alpha cutoff", Range (0,1)) = 0.0
    9. }
    10.  
    11. SubShader {
    12.     Tags { [COLOR="red"]"Queue" = "Geometry+1"[/COLOR] "RenderType" = "Opaque" }
    13.    
    14. [COLOR="red"]       Pass {         
    15.             Tags { "LightMode" = "Always" }
    16.             AlphaTest Greater [_Cutoff]
    17.             ZWrite Off
    18.             ZTest Greater
    19.            
    20.             SetTexture [_MainTex] {
    21.                 constantColor [_Indicator]
    22.                 combine constant, texture
    23.             }
    24.         }[/COLOR]
    25.  
    26. CGPROGRAM
    27. #pragma surface surf Ramp alphatest:_Cutoff
    28.  
    29. uniform float4 _Color;
    30. uniform float4 _Indicator;
    31. uniform sampler2D _MainTex;
    32. uniform sampler2D _WrapTex;
    33. uniform sampler2D _IndicatorTex;
    34.  
    35. half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
    36.     half NdotL = dot (s.Normal, lightDir);
    37.     half diff = NdotL * 0.5 + 0.5;
    38.     half3 ramp = tex2D (_WrapTex, float2(diff)).rgb;
    39.     half4 c;
    40.     c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
    41.     c.a = s.Alpha;
    42.     return c;
    43. }
    44.  
    45. struct Input {
    46.     float2 uv_MainTex;
    47.     float2 uv_BumpMap;
    48.     float3 viewDir;
    49. };
    50.  
    51. void surf (Input IN, inout SurfaceOutput o) {
    52.     o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Color;
    53.     o.Emission = tex2D ( _IndicatorTex, IN.uv_MainTex).rgb * _Indicator * 2;
    54.    
    55.     half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
    56.     o.Emission += tex2D ( _MainTex, IN.uv_MainTex).rgb * 2 * pow (rim, 3.0);
    57.     o.Alpha = tex2D ( _MainTex, IN.uv_MainTex).a;
    58. }
    59.  
    60. ENDCG
    61.  
    62. }
    63.  
    64. Fallback " Glossy", 0
    65.  
    66. }
     
    kosowski and Arkade like this.
  11. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
  12. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Wow, so we can combine Fixed Shader with Surface shader? This is interesting...
     
  13. increpare

    increpare

    Joined:
    Nov 17, 2010
    Posts:
    151
    Thanks for posting this, sycle : )
     
  14. HolBol

    HolBol

    Joined:
    Feb 9, 2010
    Posts:
    2,887
    Hey, is there anyone who can add this to the toon lighted shader? It would be really useful- You can have a dollar or two for doing it ;).
    OR here, to save me a few bucks, would this part of code:
    Code (csharp):
    1. Pass {         
    2.             Tags { "LightMode" = "Always" }
    3.             AlphaTest Greater [_Cutoff]
    4.             ZWrite Off
    5.             ZTest Greater
    6.            
    7.             SetTexture [_MainTex] {
    8.                 constantColor [_Indicator]
    9.                 combine constant, texture
    10.             }
    11.         }
    added into any shader using the same format work? I don't need the texture, just the colour, so if I edited the SetTexture lines out, would it work?


    EDIT: Forget it, I got it to work! YES!
     
    Last edited: Jul 15, 2011
  15. bem13

    bem13

    Joined:
    Jul 1, 2010
    Posts:
    64
    Exactly what I was looking for, ChickenLord and FarFarer!

    If this trick works it should definitely added to the documentation right next to where it mentions that surf shaders cannot be used in Pass {} blocks. And if it is already there, added with underlining :)
     
    macrod likes this.
  16. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    I think the reason the documents have been a little vague about using surface shaders in multiple passes is because although it does work there are some cases where it becomes a little unpredictable.

    For instance if your first pass is using the geometry render queue and the second in a latter one such as transparent, the order of the transparent pass wont play well with other transparent objects( in my experience it no longer draws from back to front ).

    Switching between deferred and forward passes also is unpredictable ( which i totally accept)

    Another weird one Ive found is that Zwrites are ignored on multi pass transparent surface shaders as well.

    Just thought id mention these to save others pulling their hair out.

    Cheers
    Brn
     
  17. thorbrian

    thorbrian

    Joined:
    Aug 26, 2010
    Posts:
    23
    I've been playing around with multiple passes, and I think what's actually going on is that each Subshader can only have one set of Tags associated with it, and it just takes the first definition for each tag mentioned and uses those values for all passes - meaning you can't have multiple passes in different queues in a single shader (I'm not able to anyways). So the reason the back to front thing was messed up was probably both your geometry pass and the transparent pass of your shader rendered in the geometry layer, making it so your transparent pass was below all other transparent items.

    ... the way this is screwing things up for me, is IgnoreProjector - I want to have a multi pass shader where the self-illuminated parts of the scene render above blob shadow projectors, and it's not working because I can't make the self illuminated part render without the projector :(
     
  18. Dev.D1

    Dev.D1

    Joined:
    Dec 29, 2010
    Posts:
    99
    @thorbrian: Were you able to resolve this issue? Or did you have to resort to using multiple materials?
     
  19. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    Personally I haven't found a way around mixing Deferred and Forward passes or the render queue issue. Its a pity because it would open up many of possibility's.
     
  20. Steven-1

    Steven-1

    Joined:
    Sep 11, 2010
    Posts:
    471
    Is it possible to combine multiple surface shaders?
    As opposed to combining a surface shader with non- surface shaders.
     
    Last edited: Jan 30, 2012
  21. CharlesBarros

    CharlesBarros

    Joined:
    Nov 17, 2011
    Posts:
    61
    I understand what Sycle did but I can't use the custom tag on Unity 4. And I can't just put the pass after the surface shader because it will consider the object mesh itself in the ztest greater. Anyone knows how to get it work?
     
  22. CharlesBarros

    CharlesBarros

    Joined:
    Nov 17, 2011
    Posts:
    61
    Nevermind, I got it.

    Here follows a sample (based on what Sycle and Farfarer wrote) that illustrate how to use multi pass with 3 different ways to write shaders in unity. Just uncomment the pass blocks to see the effect.

    $results.png

    Code (csharp):
    1. Shader "Character Indicator"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Main Color", Color) = (1,1,1,1)
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.         _Indicator ("Indicator Color", Color) = (1,1,1,1)
    8.         _Cutoff ("Alpha cutoff", Range (0,1)) = 0.0
    9.     }
    10.    
    11.     SubShader
    12.     {
    13.         Tags { "Queue" = "Geometry+1" "RenderType" = "Opaque" }
    14.  
    15.         CGPROGRAM
    16.         #pragma surface surf BlinnPhong alphatest:_Cutoff
    17.        
    18.         uniform float4 _Color;
    19.         uniform float4 _Indicator;
    20.         uniform sampler2D _MainTex;
    21.          
    22.         struct Input
    23.         {
    24.             float2 uv_MainTex;
    25.             float3 viewDir;
    26.         };
    27.        
    28.         void surf (Input IN, inout SurfaceOutput o)
    29.         {
    30.             o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Color;
    31.         }
    32.         ENDCG
    33.  
    34. // Pass: Surface SHADER
    35. //      ZWrite Off
    36. //      ZTest Greater
    37. //      Blend DstColor Zero
    38. //     
    39. //      CGPROGRAM
    40. //      #pragma surface surf BlinnPhong
    41. //      uniform float4 _Color;
    42. //      uniform float4 _Indicator;
    43. //      uniform sampler2D _MainTex;
    44. //
    45. //      struct Input
    46. //      {
    47. //          float2 uv_MainTex;
    48. //          float3 viewDir;
    49. //      };
    50. //     
    51. //      void surf (Input IN, inout SurfaceOutput o)
    52. //      {
    53. //          //o.Albedo =_Indicator;
    54. //          o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Indicator;
    55. //      }
    56. //      ENDCG  
    57.  
    58. // Pass: CG SHADER
    59. //      Pass
    60. //        {
    61. //          Tags { "LightMode" = "Always" }
    62. //          AlphaTest Greater [_Cutoff]
    63. //          ZWrite Off
    64. //          ZTest Greater
    65. //         
    66. //            CGPROGRAM
    67. //            #pragma vertex vert
    68. //            #pragma fragment frag
    69. //            #pragma fragmentoption ARB_precision_hint_fastest
    70. //            #include "UnityCG.cginc"
    71. //
    72. //          sampler2D _MainTex;
    73. //          float4 _MainTex_ST;
    74. //          uniform float4 _Indicator;
    75. //         
    76. //            struct v2f
    77. //            {
    78. //                float4 pos          : POSITION;
    79. //                float2 uv           : TEXCOORD1;
    80. //            };
    81. //
    82. //            v2f vert (appdata_full v)
    83. //            {
    84. //                v2f o;
    85. //                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);    
    86. //                o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
    87. //                return o;
    88. //            }
    89. //            
    90. //            half4 frag( v2f i ) : COLOR
    91. //            {
    92. //              half4 texcol = tex2D (_MainTex, i.uv);
    93. //                  return texcol * _Indicator;
    94. //            }
    95. //              ENDCG          
    96. //        }
    97.        
    98. // Pass: Fixed pipeline
    99. //        Pass
    100. //      {          
    101. //          Tags { "LightMode" = "Always" }
    102. //          AlphaTest Greater [_Cutoff]
    103. //          ZWrite Off
    104. //          ZTest Greater
    105. // 
    106. //          SetTexture [_MainTex]
    107. //          {
    108. //              constantColor [_Indicator]
    109. //              //combine constant, texture
    110. //              combine constant* texture
    111. //          }
    112. //      }
    113.        
    114.     }
    115.  
    116.  
    117.    
    118.     Fallback " Glossy", 0
    119.  
    120. }
     

    Attached Files:

    Last edited: Apr 21, 2013
  23. calbar

    calbar

    Joined:
    May 18, 2013
    Posts:
    10
    Charles, your single post has helped my understanding more than the last few hours of research. Thanks a million for taking the time to post this code and the accompanying images. :D
     
  24. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    This is an older post but is exactly what I am looking for however I cant seem to get it working.

    The double surface trick works but not the CG or fixed function pass and what I need to do is run a surface shader for non occluded parts then follow up with a CG rendering only the occluded parts ...

    I tryed using the code you have posted above and commented each 1 by 1 again the surface surface works as expect but not surface + CG or surface + fixed

    PS: I have worked around this by just using surface shaders as opposed to a vert/frag CG; also I note you need to switch the order

    below is an example

    Code (csharp):
    1.  
    2. SubShader
    3.  {
    4.   Tags { "Queue" = "AlphaTest" "RenderType" = "Opaque" }
    5.  
    6.     ZWrite Off
    7.     ZTest Greater
    8.     Blend One One
    9.      
    10.     CGPROGRAM
    11.     #pragma surface surf Lambert
    12.     struct Input {
    13.       float2 uv_MainTex;
    14.       float2 uv_BumpMap;
    15.       float3 viewDir;
    16.     };
    17.     //sampler2D _MainTex;
    18.     sampler2D _BumpMap;
    19.     float4 _RimColor;
    20.     float _RimWidth;
    21.     void surf (Input IN, inout SurfaceOutput o) {
    22.       o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    23.       half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
    24.       o.Emission = _RimColor.rgb * pow (rim, _RimWidth);
    25.     }
    26.     ENDCG  
    27.    
    28.     ZWrite On
    29.     ZTest LEqual
    30.     Blend Off
    31.    
    32.     CGPROGRAM
    33.     #pragma surface surf Lambert
    34.  
    35.     sampler2D _MainTex;
    36.     sampler2D _BumpMap;
    37.     fixed4 _Color;
    38.  
    39.     struct Input {
    40.         float2 uv_MainTex;
    41.         float2 uv_BumpMap;
    42.     };
    43.  
    44.     void surf (Input IN, inout SurfaceOutput o) {
    45.         fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    46.         o.Albedo = c.rgb;
    47.         o.Alpha = c.a;
    48.         o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    49.     }
    50.     ENDCG
    51.  }
    52.  
    This does the following
    $RimBumped.PNG
     
    Last edited: Feb 24, 2014
    Arkade likes this.
  25. dot_entity

    dot_entity

    Joined:
    Nov 2, 2012
    Posts:
    86
    Thank you lodendsg! Given my beginner's understanding, I was pulling my hair to realize, why I cannot combine a CG pass with a depth offset parameter, no mater what combinations of code I tried. While the same time, due to the 3.4 version of Unity that I currently use, it doesn't seem to work with the structure the older posts had recommended. It is your example that showed me the correct way, although I'm not sure that I fully understand, how the order of code blocks is significant for a proper function...:confused:
     
  26. Earlybird

    Earlybird

    Joined:
    Mar 30, 2013
    Posts:
    14
    Can i ask is this possible using an Indie version of Unity?
    im pretty new to shaders but can understnad Surface Shaders but I cant any of these shaders to work?

    Also are these Multi pass more expensive because they use surface shaders?

    Here's what I have so far:
    Code (csharp):
    1. Shader "Custom/MultiPass Surface Shader" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1,1,1,1)
    4.     _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    5.     _BumpMap ("Bump (RGB) Gloss (A)", 2D) = "bump" {}
    6.     _RimColor ("Rim Color", Range (0, 1)) = 0.5
    7.     _RimWidth ("Rim Width", Range (0, 1)) = 0.5
    8. }
    9.  
    10. SubShader
    11.  {
    12.   Tags { "Queue" = "AlphaTest" "RenderType" = "Opaque" }
    13.  
    14.     ZWrite Off
    15.     ZTest Greater
    16.     Blend One One
    17.  
    18.     CGPROGRAM
    19.     #pragma surface surf Lambert
    20.  
    21.     struct Input {
    22.       float2 uv_MainTex;
    23.       float2 uv_BumpMap;
    24.       float3 viewDir;
    25.     };
    26.  
    27.     //sampler2D _MainTex;
    28.     sampler2D _BumpMap;
    29.     float4 _RimColor;
    30.     float _RimWidth;
    31.  
    32.     void surf (Input IN, inout SurfaceOutput o) {
    33.       o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    34.       half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
    35.       o.Emission = _RimColor.rgb * pow (rim, _RimWidth);
    36.     }
    37.  
    38.     ENDCG  
    39.  
    40.    
    41.  
    42.     ZWrite On
    43.     ZTest LEqual
    44.     Blend Off
    45.  
    46.     CGPROGRAM
    47.     #pragma surface surf Lambert
    48.  
    49.     sampler2D _MainTex;
    50.     sampler2D _BumpMap;
    51.     fixed4 _Color;
    52.  
    53.     struct Input {
    54.         float2 uv_MainTex;
    55.         float2 uv_BumpMap;
    56.     };
    57.  
    58.     void surf (Input IN, inout SurfaceOutput o) {
    59.         fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    60.         o.Albedo = c.rgb;
    61.         o.Alpha = c.a;
    62.         o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    63.     }
    64.  
    65.     ENDCG
    66.  
    67. Fallback "Diffuse"
    68.  
    69.  }
    and here is the error I keep getting?
    "Shader error in 'Custom/MultiPass Surface Shader': Parse error: syntax error at line 107"

    Any help?
    Thanks.
     
  27. OneWayRoad

    OneWayRoad

    Joined:
    Jul 27, 2013
    Posts:
    26
    hey farfarer, i just tested your hair shader. i just noticed that the aniso color(white) is just too strong when not lighted. the darker the area the stronger the aniso is. is there a way to control the intensity of aniso?
     
  28. antislash

    antislash

    Joined:
    Apr 23, 2015
    Posts:
    646
    hey, i'm folowing this thread with great interest and it helped me to successfully achieve some effects.

    i have a question : is there a way to keep a variable over the passes ?
     
    Last edited: Jun 1, 2015
  29. varfare

    varfare

    Joined:
    Feb 12, 2013
    Posts:
    227
    As far as I know - it is not possible. You can pass data from vertex program to fragment program or from script to shader but not from pass to pass.
     
    antislash likes this.
  30. antislash

    antislash

    Joined:
    Apr 23, 2015
    Posts:
    646
    mmm too bad, may be a setPixel/getPixel to store a value ? or is ot too slow ?

    thanks a lot
     
    Last edited: Jun 2, 2015
  31. varfare

    varfare

    Joined:
    Feb 12, 2013
    Posts:
    227
    SetPixel/GetPixel is really slow but if it's not a problem for you then why not. In general- reading data from GPU is tricky. I believe that you can do that with Compute Shaders but they are probably not what you are looking for. Also, if you need a specific variable to be passed for both passes you may want to create C# script, compute whatever you need to and pass it to the shader/material.
     
  32. antislash

    antislash

    Joined:
    Apr 23, 2015
    Posts:
    646
    i'll follow your advice and avoid set/get pixel then
    thanks a lot
     
  33. Zardify

    Zardify

    Joined:
    Jul 15, 2015
    Posts:
    20
    Old post, but look into Stencil shaders where you can easily assign a 0-255 integer value to the pixels of each pass and then use those values for basic calculations in other passes. :)
     
    antislash likes this.
  34. varfare

    varfare

    Joined:
    Feb 12, 2013
    Posts:
    227
    You can write your image effect or compute buffer - store your values in custom render target and assign them for your passes (blur effect is done this way). Compute buffers are also an option since they can return calculation values or textures back to the CPU.
     
  35. Zardify

    Zardify

    Joined:
    Jul 15, 2015
    Posts:
    20
    This whole shader thingy can get so complicated in minutes! :D I just started learning but after every line of code i feel like i "need more"...
     
  36. AlexTemina

    AlexTemina

    Joined:
    Feb 19, 2014
    Posts:
    44
    Hi!

    Sorry to bump this up, but I have a question that may help a lot people that comes and see this, which is a rather famous post in the forum.

    I'm trying to do a multipass shader, but I need the _MainTex of the first pass, since I'm making one operation and then, once the maintex is done, do another one, but this doesn't work because I get the old maintex again.

    How can I achieve that?

    Thanks!
     
  37. Marco-Sperling

    Marco-Sperling

    Joined:
    Mar 5, 2012
    Posts:
    620
    You mean, you manipulate the fragments of the _MainTex in the first pass and want to access the manipulated _MainTex in the second pass?
    Won't work. Since you are not manipulating any of the input texture data. You are manipulating the output to the framebuffer in a shader. The input textures are mere variables that help shaping the output.
    Passes only blend what's already in the framebuffer with the output they themself generate in a specified manner.
    And that's the answer to your challenge. You can either do it with clever pass blending. Or if you absolutely need to manipulate texture data for usage in subsequent passes because there is no blend operation that suits your requirements you probably need to do this with rendertextures and Graphics.Blit()
     
    migmanson, Wizz-Art-3D and AlexTemina like this.
  38. monotoan

    monotoan

    Joined:
    Feb 13, 2015
    Posts:
    11
    This solution worked for me!

     
  39. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,546
    This doesn't seem to work for some reason when mixing surface shader and fragment shaders.

    I have two surface shaders passes, and those work fine, but the fragment pass after them fails to do anything, even if I turn off ZTest and ZWrite. I've tried making it even wildly adjust the vertices to grow the mesh in that final pass, but nothing happens... It's like Unity is throwing away that fragment pass because of the surface shader passes being there. Doesn't matter if I put the fragment pass before them or after.

    Code (CSharp):
    1. Shader "Invertex/PassTest"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Front Tint", Color) = (1,1,1,1)
    6.         _BackColor ("Rear Tiny", Color) = (1,1,1,1)
    7.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    8.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    9.         _Metallic ("Metallic", Range(0,1)) = 0.0
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "RenderType"="Geometry" }
    14.         LOD 200
    15.  
    16. //Front regular pass
    17.         ZWrite true
    18.         Cull back
    19.         ZTest true
    20.  
    21.         CGPROGRAM
    22.         #pragma surface surf Standard vertex:vert addshadow
    23.         #pragma target 3.0
    24.  
    25.         sampler2D _MainTex;
    26.         half _Glossiness;
    27.         half _Metallic;
    28.         fixed4 _Color;
    29.  
    30.         struct Input
    31.         {
    32.             float2 uv_MainTex;
    33.         };
    34.  
    35.         void vert(inout appdata_full v, out Input o)
    36.         {
    37.             UNITY_INITIALIZE_OUTPUT(Input, o);
    38.         }
    39.  
    40.         void surf(Input IN, inout SurfaceOutputStandard o)
    41.         {
    42.             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    43.             o.Albedo = c.rgb;
    44.             o.Metallic = _Metallic;
    45.             o.Smoothness = _Glossiness;
    46.         }
    47.         ENDCG
    48.  
    49. //Rear pass.
    50.         ZTest on
    51.         ZWrite on
    52.         Cull Front
    53.         CGPROGRAM
    54.         #pragma surface surf Standard vertex:vert
    55.         #pragma target 3.0
    56.  
    57.         sampler2D _MainTex;
    58.         half _Glossiness;
    59.         half _Metallic;
    60.         fixed4 _BackColor;
    61.  
    62.         struct Input
    63.         {
    64.             float2 uv_MainTex;
    65.         };
    66.  
    67.         void vert(inout appdata_full v, out Input o)
    68.         {
    69.             UNITY_INITIALIZE_OUTPUT(Input, o);
    70.         }
    71.  
    72.         void surf (Input IN, inout SurfaceOutputStandard o)
    73.         {
    74.             o.Albedo = tex2D(_MainTex, IN.uv_MainTex) * _BackColor;
    75.             o.Metallic = _Metallic;
    76.             o.Smoothness = _Glossiness;
    77.         }
    78.         ENDCG
    79.  
    80.         Pass
    81.         {
    82.             ZWrite true
    83.             ZTest false
    84.             Cull off
    85.  
    86.             CGPROGRAM
    87.             #pragma vertex vert
    88.             #pragma fragment frag
    89.            
    90.             #include "UnityCG.cginc"
    91.  
    92.             struct appdata
    93.             {
    94.                 float4 vertex : POSITION;
    95.                 float2 uv : TEXCOORD0;
    96.             };
    97.  
    98.             struct v2f
    99.             {
    100.                 float2 uv : TEXCOORD0;
    101.                 float4 vertex : SV_POSITION;
    102.             };
    103.  
    104.             sampler2D _MainTex;
    105.             float4 _MainTex_ST;
    106.            
    107.             v2f vert (appdata v)
    108.             {
    109.                 v2f o;
    110.                 v.vertex += float4(4, 5, 3, 0);
    111.                 o.vertex = UnityObjectToClipPos(v.vertex);
    112.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    113.                 return o;
    114.             }
    115.            
    116.             fixed4 frag (v2f i) : SV_Target
    117.             {
    118.                 fixed4 col = tex2D(_MainTex, i.uv);
    119.                 return col + float4(3,3,5,2);
    120.             }
    121.             ENDCG
    122.         }
    123.     }
    124.     FallBack "Diffuse"
    125. }

    Unity 2017.3.1.
     
  40. m0nkeybl1tz

    m0nkeybl1tz

    Joined:
    Feb 10, 2013
    Posts:
    35
    Hey all, sorry to dig up an old thread but it's been really helpful so far in helping me get to where I want.

    The problem I'm running into is that I want to have a two-pass Surface shader, where the first pass only responds to Directional lights, and the second pass responds only to Spot/Point lights.

    So far, I've tried two solutions that I thought would work. The first is using Pass Tags to switch between ForwardBase (Directional only) and ForwardAdd (Spot and Point) like so:

    Code (CSharp):
    1. Tags {"LightMode"="ForwardBase"}
    2.  
    3. CGPROGRAM
    4. ...
    5. ENDCG
    6.  
    7. Tags {"LightMode"="ForwardAdd"}
    8.  
    9. ZWrite Off
    10. ZTest Greater
    11. Blend DstColor Zero
    12.  
    13. CGPROGRAM
    14. ...
    15. ENDCG
    This just makes everything black.

    The other thing I've tried is using Compile Directives to turn off Forward Add for the first pass, like so:

    Code (CSharp):
    1. CGPROGRAM
    2. #pragma surface surf Standard fullforwardshadows noforwardadd
    3. ...
    4. ENDCG
    5.  
    6. ZWrite Off
    7. ZTest Greater
    8. Blend DstColor Zero
    9.  
    10. CGPROGRAM
    11. #pragma surface surf Standard fullforwardshadows
    12. ...
    13. ENDCG
    This time it renders, but now it doesn't respond to Spot or Point Lights for either pass.

    Is there any way to have the Compile Directive work for just the one pass? Or to make the second pass a Forward Add pass?
     
  41. AAAAAAAAAE

    AAAAAAAAAE

    Joined:
    Jun 8, 2013
    Posts:
    100
    Thankyou very much.