Search Unity

How would you unify these two shaders

Discussion in 'Shaders' started by Smitzo, May 26, 2020.

  1. Smitzo

    Smitzo

    Joined:
    Mar 23, 2019
    Posts:
    11
    I have a dithering transparency shader and a only cast shadow shader.
    The dithering transparency shader makes the object fade like in the picture.
    The only cast shadow shader makes an effect similar to the "shadow only" option from MeshRenderer. It shows ONLY the CAST shadow. No received shadow is shown.

    The effect I am trying to get is that only the shadows received on the surface of the object fade with the object. But the shadow the object itself casts should stay.

    I did achieve this by applying both shaders to the same MeshRenderer. But this means two materials per MeshRenderer. Not ideal.

    Any ideas how I could make this work with only one shader or any suggestions to optimize this?

    upload_2020-5-26_14-47-23.png

    DITHERTING TRANSPARENCY SHADER:
    Code (CSharp):
    1. Shader "TEST/Dithering (Advanced)"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Main Color", Color) = (1,1,1,0)
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.         _Transparency ("Transparency", Range(0,1)) = 1.0
    8.     }
    9.    
    10.     SubShader
    11.     {
    12.         Tags { "RederType"="Opaque"}
    13.         LOD 200
    14.        
    15.         Pass
    16.         {
    17.             Tags {"LightMode"="ForwardBase"}
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
    22.             #include "UnityCG.cginc"
    23.             #include "Lighting.cginc"
    24.             #include "AutoLight.cginc"
    25.  
    26.             struct v2f
    27.             {
    28.                 float2 uv : TEXCOORD0;
    29.                 SHADOW_COORDS(1)
    30.                 fixed3 diff : COLOR0;
    31.                 fixed3 ambient : COLOR1;
    32.                 float4 pos : SV_POSITION;
    33.             };
    34.            
    35.             half _Transparency;
    36.  
    37.             v2f vert (appdata_base v)
    38.             {
    39.                 v2f o;
    40.                 o.pos = UnityObjectToClipPos(v.vertex);
    41.                 o.uv = v.texcoord;
    42.                 half3 worldNormal = UnityObjectToWorldNormal(v.normal);
    43.                 half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    44.                 o.diff = nl * _LightColor0.rgb;
    45.                 o.ambient = ShadeSH9(half4(worldNormal,1));
    46.                 TRANSFER_SHADOW(o)
    47.                 return o;
    48.             }
    49.  
    50.             sampler2D _MainTex;
    51.  
    52.             fixed4 frag (v2f i) : SV_Target
    53.             {
    54.                 fixed4 col = tex2D(_MainTex, i.uv);
    55.                 fixed shadow = SHADOW_ATTENUATION(i);
    56.                
    57.                 float4x4 thresholdMatrix =
    58.                 { 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
    59.                 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
    60.                 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
    61.                 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
    62.                 };
    63.                 float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
    64.                 float2 position = i.pos.xy / i.pos.w;
    65.                 position *= _ScreenParams.xy;
    66.                 clip(_Transparency - thresholdMatrix[fmod(position.x, 4)] * _RowAccess[fmod(position.y, 4)]);
    67.  
    68.                 fixed3 lighting = i.diff * shadow + i.ambient;
    69.                 col.rgb *= lighting;
    70.                 return col;
    71.             }
    72.             ENDCG
    73.         }
    74.  
    75.         Pass
    76.         {
    77.             Name "FORWARD"
    78.             Tags { "LightMode" = "ForwardAdd" }
    79.             ZWrite Off Blend One One
    80.  
    81.             CGPROGRAM
    82.             #pragma vertex vert
    83.             #pragma fragment frag
    84.             #pragma multi_compile_fwdadd_fullshadows
    85.             #include "UnityCG.cginc"
    86.             #include "Lighting.cginc"
    87.             #include "AutoLight.cginc"
    88.  
    89.             struct v2f
    90.             {
    91.                 float2 uv : TEXCOORD2;
    92.                 float4 pos : SV_POSITION;
    93.                 float3 worldPos : TEXCOORD0;
    94.                 SHADOW_COORDS(1)
    95.             };
    96.  
    97.             half _Transparency;
    98.  
    99.             v2f vert (appdata_full v)
    100.             {
    101.                 v2f o;
    102.                 o.pos = UnityObjectToClipPos (v.vertex);
    103.                 o.uv = v.texcoord;
    104.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    105.                 TRANSFER_SHADOW(o);
    106.                 return o;
    107.             }
    108.  
    109.             sampler2D _MainTex;
    110.  
    111.             fixed4 frag (v2f i) : SV_Target
    112.             {
    113.                 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)
    114.                 fixed4 col = tex2D(_MainTex, i.uv) * atten;
    115.                
    116.                 float4x4 thresholdMatrix =
    117.                 { 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
    118.                 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
    119.                 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
    120.                 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
    121.                 };
    122.                 float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
    123.                 float2 position = i.pos.xy / i.pos.w;
    124.                 position *= _ScreenParams.xy;
    125.                 clip(_Transparency - thresholdMatrix[fmod(position.x, 4)] * _RowAccess[fmod(position.y, 4)]);
    126.  
    127.                 col.rgb *= _LightColor0.rgb;
    128.                 return col;
    129.             }
    130.  
    131.             ENDCG
    132.         }
    133.        
    134.         Pass
    135.         {
    136.             Tags { "Queue" = "Geometry" "LightMode" = "ShadowCaster" }
    137.  
    138.             CGPROGRAM
    139.             #pragma vertex vert
    140.             #pragma fragment frag
    141.            
    142.             #include "UnityCG.cginc"
    143.            
    144.             struct appdata
    145.             {
    146.                 float4 vertex : POSITION;
    147.                 float2 uv : TEXCOORD0;
    148.                 float4 col : COLOR;
    149.             };
    150.            
    151.             struct v2f
    152.             {
    153.                 float2 uv : TEXCOORD0;
    154.                 float4 vertex : SV_POSITION;
    155.                 float4 col : COLOR;
    156.                 float4 screenPos : TEXCOORD1;
    157.             };
    158.            
    159.             sampler2D _MainTex;
    160.             float4 _MainTex_ST;
    161.             half _Transparency;
    162.            
    163.             v2f vert (appdata v)
    164.             {
    165.                 v2f o;
    166.                 o.vertex = UnityObjectToClipPos(v.vertex);
    167.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    168.                 o.col = v.col;
    169.                 o.screenPos = ComputeScreenPos(o.vertex);
    170.                 return o;
    171.             }
    172.            
    173.             fixed4 frag (v2f i) : SV_Target
    174.             {
    175.                 fixed4 col = tex2D(_MainTex, i.uv);
    176.                 float4x4 thresholdMatrix =
    177.                 { 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
    178.                 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
    179.                 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
    180.                 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
    181.                 };
    182.                 float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
    183.                 float2 pos = i.screenPos.xy / i.screenPos.w;
    184.                 pos *= _ScreenParams.xy;
    185.                 clip(_Transparency - thresholdMatrix[fmod(pos.x, 4)] * _RowAccess[fmod(pos.y, 4)]);
    186.  
    187.                 return col;
    188.             }
    189.             ENDCG
    190.         }
    191.     }
    192. }
    ONLY CAST SHADOW SHADER:
    Code (CSharp):
    1. Shader "TEST/Only Cast Shadow"
    2. {
    3.     SubShader
    4.     {
    5.         Tags{ "Queue" = "Transparent" "LightMode" = "ShadowCaster" }
    6.         Pass
    7.         {
    8.             CGPROGRAM
    9.    
    10.             #pragma vertex vert
    11.             #pragma fragment frag
    12.             #pragma multi_compile_shadowcaster
    13.             #pragma fragmentoption ARB_precision_hint_fastest
    14.            
    15.             #include "UnityCG.cginc"
    16.        
    17.             struct v2f
    18.             {
    19.                 V2F_SHADOW_CASTER;
    20.             };
    21.        
    22.             v2f vert(appdata_base v)
    23.             {
    24.                 v2f o;
    25.                 TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    26.                 return o;
    27.             }
    28.        
    29.             float4 frag(v2f i) : COLOR
    30.             {
    31.                 SHADOW_CASTER_FRAGMENT(i)
    32.             }
    33.             ENDCG
    34.         }
    35.     }
    36.     FallBack Off
    37. }
     
  2. uwdlg

    uwdlg

    Joined:
    Jan 16, 2017
    Posts:
    150
    Assuming I understood what you want correctly (having the object cast shadows like in the top left image regardless of the _Transparency value (?)), you would just leave out the dithering part in the ShadowCaster pass of the Dithering shader (i.e. remove lines 176-185). To me, that recreates the visual effect of having both materials attached with only one material.
    Hope that helps.
     
  3. Smitzo

    Smitzo

    Joined:
    Mar 23, 2019
    Posts:
    11
    Hi uwdlg!
    Thank you for your reply.
    When I do what you suggested, the shadow casted by the object stays, but the shadow it receives also stays.
    What I am looking for is a solution where the shadow casted by the object stays unchanged, while the shadows it receives on it's surface dither and become "invisible".

    For example, imagine you have a room with a cealing in a top down game. You want that cealing to "disappear" when the player enters, so you can see the player. However, you do not want the Directional Light to light the interior. If it was a "darker" room because of the cealing, it should stay like that. So the shadow casted by the cealing must stay.
    On top of that, the cealing receives shadows from for example some trees outside. When the cealing dithers invisible the shadows it receives from the trees on its surface, should dither aswell.

    I'm probably really bad at explaining it and I apologize for any misunderstanding!

    Let me know if you have any questions.
     
  4. uwdlg

    uwdlg

    Joined:
    Jan 16, 2017
    Posts:
    150
    The received shadows are being dithered, the call to
    clip()
    discards pixels independently from whether they are receiving shadows or not.
    You might be expecting a different visual outcome because shadows being cast from a material using the original unmodified shader onto another object with the same material/shader are effectively dithered twice: once in the ShadowCaster pass and again in the ForwardBase pass when they are received.

    To get something that looks more like the original shader for received shadows, you would maybe have to call
    clip()
    again with a different dithering pattern if the value of
    fixed shadow = SHADOW_ATTENUATION(i);
    (line 56) is below some threshold. But that might not be a great way of doing that in terms of efficiency and might not look desirable, as areas that aren't directly "receiving" a shadow but are just facing away from a light source would also be affected (which isn't the case with the original shader).
     
  5. Smitzo

    Smitzo

    Joined:
    Mar 23, 2019
    Posts:
    11
    You say that the received shadows "are" being dithered, but if I remove the ShadowCaster pass, I get a fully transparent wall when the pixels have all been discarded, however the cast shadow stays and the received shadow also. So it looks like a transparent wall that still has full shadows on and from it.
     
  6. uwdlg

    uwdlg

    Joined:
    Jan 16, 2017
    Posts:
    150
    That's strange. Removing the ShadowCaster should prevent any shadows from being cast, and that's the case when I do it on my end. Do you have a fallback shader specified at the bottom of the shader file from which a ShadowCaster pass could be pulled?
    The other part is equally strange. Turning the transparency down to zero means all pixels are being discarded in the fragment shader, so there shouldn't be any received shadows (which again is the case on my end).
    Could you post the shader that gives you those results?
     
  7. Smitzo

    Smitzo

    Joined:
    Mar 23, 2019
    Posts:
    11
    So I played a bit more around with it.
    I think I can narrow down the problem to this:

    1) I get cast shadow and received shadow, the received shadow dithers as wanted, the cast shadow stays as wanted, but when the object is completelly dithered the shadows cast by other objects from directional light are not rendered (objects behind the dithered object). The dithered object also doesn't completelly disappear because one face opposing directional light stays shadowed.

    OR

    2) I get cast shadow that stays, the object completelly disappears when fully dithered, I can see the shadows behind it cast by objects from spotllights and directional lights, BUT there is no received shadow on the object I want to dither.

    Here the code I'm using.

    Code (CSharp):
    1. Shader "TEST/Dithering Transparency"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Main Color", Color) = (1,1,1,0)
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.         _Transparency ("Transparency", Range(0,1)) = 1.0
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Tags { "RederType"="Opaque"}
    13.         LOD 200
    14.      
    15.         Pass
    16.         {
    17.             Tags {"LightMode"="ForwardBase"}
    18.             CGPROGRAM
    19.             #pragma vertex vert full
    20.             #pragma fragment frag
    21.             #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
    22.             #include "UnityCG.cginc"
    23.             #include "Lighting.cginc"
    24.             #include "AutoLight.cginc"
    25.             struct v2f
    26.             {
    27.                 float2 uv : TEXCOORD0;
    28.                 SHADOW_COORDS(1)
    29.                 fixed3 diff : COLOR0;
    30.                 fixed3 ambient : COLOR1;
    31.                 float4 pos : SV_POSITION;
    32.             };
    33.          
    34.             half _Transparency;
    35.             v2f vert (appdata_base v)
    36.             {
    37.                 v2f o;
    38.                 o.pos = UnityObjectToClipPos(v.vertex);
    39.                 o.uv = v.texcoord;
    40.                 half3 worldNormal = UnityObjectToWorldNormal(v.normal);
    41.                 half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    42.                 o.diff = nl * _LightColor0.rgb;
    43.                 o.ambient = ShadeSH9(half4(worldNormal,1));
    44.                 TRANSFER_SHADOW(o)
    45.                 return o;
    46.             }
    47.             sampler2D _MainTex;
    48.             fixed4 frag (v2f i) : SV_Target
    49.             {
    50.                 fixed4 col = tex2D(_MainTex, i.uv);
    51.                 fixed shadow = SHADOW_ATTENUATION(i);
    52.              
    53.                 float4x4 thresholdMatrix =
    54.                 { 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
    55.                 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
    56.                 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
    57.                 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
    58.                 };
    59.                 //float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
    60.                 float2 position = i.pos.xy / i.pos.w;
    61.                 position *= _ScreenParams.xy;
    62.                 clip(_Transparency - thresholdMatrix[position.x % 4] [position.y % 4]);
    63.                 fixed3 lighting = i.diff * shadow + i.ambient;
    64.                 col.rgb *= lighting;
    65.                 return col;
    66.             }
    67.             ENDCG
    68.         }
    69.         Pass
    70.         {
    71.             Name "FORWARD"
    72.             Tags { "LightMode" = "ForwardAdd" }
    73.             ZWrite Off Blend One One
    74.             CGPROGRAM
    75.             #pragma vertex vert
    76.             #pragma fragment frag
    77.             #pragma multi_compile_fwdadd_fullshadows
    78.             #include "UnityCG.cginc"
    79.             #include "Lighting.cginc"
    80.             #include "AutoLight.cginc"
    81.             struct v2f
    82.             {
    83.                 float2 uv : TEXCOORD2;
    84.                 float4 pos : SV_POSITION;
    85.                 float3 worldPos : TEXCOORD0;
    86.                 SHADOW_COORDS(1)
    87.             };
    88.             half _Transparency;
    89.             v2f vert (appdata_full v)
    90.             {
    91.                 v2f o;
    92.                 o.pos = UnityObjectToClipPos (v.vertex);
    93.                 o.uv = v.texcoord;
    94.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    95.                 TRANSFER_SHADOW(o);
    96.                 return o;
    97.             }
    98.             sampler2D _MainTex;
    99.             fixed4 frag (v2f i) : SV_Target
    100.             {
    101.                 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)
    102.                 fixed4 col = tex2D(_MainTex, i.uv) * atten;
    103.              
    104.                 float4x4 thresholdMatrix =
    105.                 { 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
    106.                 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
    107.                 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
    108.                 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
    109.                 };
    110.                 //float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
    111.                 float2 position = i.pos.xy / i.pos.w;
    112.                 position *= _ScreenParams.xy;
    113.                 clip(_Transparency - thresholdMatrix[position.x % 4] [position.y % 4]);
    114.                 col.rgb *= _LightColor0.rgb;
    115.                 return col;
    116.             }
    117.             ENDCG
    118.         }
    119.  
    120.         // IF THIS, THEN CAST SHADOW BUT NO RECEIVED SHADOW
    121.         // Tags{ "Queue" = "Transparent" "LightMode" = "ShadowCaster" }
    122.         Pass
    123.         {
    124.             // IF THIS, THEN CAST SHADOW AND RECEIVE SHADOW, BUT IT DOESNT RENDER SHADOWS CAST BY OTHER OBJECTS FROM DIRECTIONAL LIGHT (OBJECTS BEHIND THE DITHERED OBJECT). YOU CAN SEE THAT THE DITHERED OBJECT ALSO HAS A SHADOWED FACE ON THE FACE OF THE OBJECT OPPOSED TO THE DIRECTIONAL LIGHT.
    125.             Tags{ "Queue" = "Transparent" "LightMode" = "ShadowCaster" }
    126.            
    127.             CGPROGRAM
    128.    
    129.             #pragma vertex vert
    130.             #pragma fragment frag
    131.             #pragma multi_compile_shadowcaster
    132.             #pragma fragmentoption ARB_precision_hint_fastest
    133.            
    134.             #include "UnityCG.cginc"
    135.        
    136.             struct v2f
    137.             {
    138.                 V2F_SHADOW_CASTER;
    139.             };
    140.        
    141.             v2f vert(appdata_base v)
    142.             {
    143.                 v2f o;
    144.                 TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    145.                 return o;
    146.             }
    147.        
    148.             float4 frag(v2f i) : COLOR
    149.             {
    150.                 SHADOW_CASTER_FRAGMENT(i)
    151.             }
    152.             ENDCG
    153.         }
    154.     }
    155. }
     
  8. uwdlg

    uwdlg

    Joined:
    Jan 16, 2017
    Posts:
    150
    Ah, sorry, I initially threw the shaders into one of my projects I had open at the time and didn't have the problems there, but it targets Android (which handles shadows differently).
    I can reproduce the behavior you get in a new project targeting Windows now. But I'm not knowledgeable enough to be able to help further I'm afraid. In order for transparency to work as expected you'd need to set the SubShader queue to Transparent (to have it rendered on top of everything else) but Unity doesn't pass the shadow map to shaders in the Transparent queue apparently (see here).
    Gonna have to defer to someone who knows more about shaders and rendering, but this could be hard to do without rolling a custom shadow mapping implementation. But then again I don't know much about the details of shadow mapping in Unity.