Alpha clip doesn't clip shadows from directional light

Version: Unity 2019.2.8f1
I have a very simple LWRP alpha clip shader currently, based on the LWRP Lit example code posted on git. The alpha gets clipped, but the shadows are unclipped.
I feel like the solution to this is very simple, I’m probably just brain farting and overlooking it.
Here are some potentially relevant pieces of my shader code:

//..........
        SubShader
        {
            Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline" "IgnoreProjector" = "True"}
            LOD 300

            Pass
            {
            Name "StandardLit"
            Tags{"LightMode" = "LightweightForward"}

            Blend[_SrcBlend][_DstBlend]
            ZTest [_ZTest]
            ZWrite[_ZWrite]
            Cull[_Cull]

            HLSLPROGRAM

            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            // unused shader_feature variants are stripped from build automatically
            #pragma shader_feature _USEVERTEXCOLOR
            #pragma shader_feature _ISROUGHNESSMAP
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICSPECGLOSSMAP
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _OCCLUSIONMAP

            #pragma shader_feature _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature _GLOSSYREFLECTIONS_OFF
            #pragma shader_feature _SPECULAR_SETUP
            #pragma shader_feature _RECEIVE_SHADOWS_OFF

            // -------------------------------------
            // Lightweight Render Pipeline keywords

            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile _ DIRLIGHTMAP_COMBINED
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile_fog

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing

            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment

            #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"

//...............

#ifdef _MAIN_LIGHT_SHADOWS
                output.shadowCoord = GetShadowCoord(vertexInput);
#endif
                // We just use the homogeneous clip position from the vertex input
                output.positionCS = vertexInput.positionCS;
                return output;
            }

            half4 LitPassFragment(Varyings input) : SV_Target
            {
                SurfaceData surfaceData;
                InitializeStandardLitSurfaceData(input.uv, surfaceData);
               
                half4 albedoMap = _BaseMap.Sample(sampler_BaseMap, input.uv);
                clip(albedoMap.a * _BaseColor.a - _Cutoff);
                half3 normalMap = UnpackNormalScale(_BumpMap.Sample(sampler_BumpMap, input.uv), _BumpScale);
                half3 metallicMap = _MetallicRoughMap.Sample(sampler_MetallicRoughMap, input.uv);
                half3 emissionMap = _EmissionMap.Sample(sampler_EmissionMap, input.uv);

                surfaceData.albedo = albedoMap.rgb;
                surfaceData.alpha = albedoMap.a;
                surfaceData.normalTS = normalMap;
                surfaceData.metallic = metallicMap.r * _MetallicScale;
                surfaceData.smoothness = clamp(1-metallicMap.g,0,1) * _RoughnessMapScale;
                //We lerp this one with 1 because a value of 0 means its fully occluded
                surfaceData.occlusion = lerp(1, metallicMap.b, _OcclusionStrength);
                surfaceData.emission = emissionMap * _EmissionColor;

//............


#ifdef _MAIN_LIGHT_SHADOWS
                Light mainLight = GetMainLight(input.shadowCoord);
#else
                Light mainLight = GetMainLight();
#endif

                // Mix diffuse GI with environment reflections.
                half3 color = GlobalIllumination(brdfData, bakedGI, surfaceData.occlusion, normalWS, viewDirectionWS);

                // LightingPhysicallyBased computes direct light contribution.
                color += LightingPhysicallyBased(brdfData, mainLight, normalWS, viewDirectionWS);

                // Additional lights loop
#ifdef _ADDITIONAL_LIGHTS

                int additionalLightsCount = GetAdditionalLightsCount();
                for (int i = 0; i < additionalLightsCount; ++i)
                {
                    Light light = GetAdditionalLight(i, positionWS);

                    color += LightingPhysicallyBased(brdfData, light, normalWS, viewDirectionWS);
                }
#endif
                // Emission
                color += surfaceData.emission;
                float fogFactor = input.positionWSAndFogFactor.w;
                color = MixFog(color, fogFactor);
                return half4(color, surfaceData.alpha);
            }
            ENDHLSL
        }

            // Used for rendering shadowmaps
            UsePass "Lightweight Render Pipeline/Lit/ShadowCaster"

            UsePass "Lightweight Render Pipeline/Lit/DepthOnly"

            // Used for Baking GI. This pass is stripped from build.
            UsePass "Lightweight Render Pipeline/Lit/Meta"
        }

        FallBack "Hidden/InternalErrorShader"
}

your shadow caster and depth only passes are missing in the code example - and these are all responsible for shadows. so i woud recommend to look into these passes. use frame debugger to see where shadows fail.

Hmm, I’m just calling the ones in the standard LWRP Lit library. Is there a bug in those somewhere that ignores clipping in the shadow phase?

The depth and shadow passes do not perform alpha clipping if the _ALPHATEST_ON keyword is disabled. For the Lit shader, this is toggled through the material GUI. If you’re making a cutout shader, then you can simply force this to be enabled at all times.

You can replace this line

#pragma shader_feature _ALPHATEST_ON

with

#define _ALPHATEST_ON

This won’t work. UsePass ignores any #define or #pragma lines from the parent shader, even if they’re in CGINCLUDE / HLSLINCLUDE blocks. When using UsePass the only way to ensure the proper keyword is set is to actually set it on the material, either via script, a keyword enum toggle property, or using the debug inspector.

The correct solution is to not use UsePass. You could use FallBack instead, but that has the same problem of ignoring the parent shader’s code defined keywords, though it should respect any set on the material. Instead copy the shadow caster and depth only passes from the other shader instead. Most actual shader code is in included .hlsl files, so the Pass is relatively simple. Really just a handful of #include and #pragma lines to define the keywords and vertex / fragment functions.

        Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}

            ZWrite On
            ZTest LEqual
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags{"LightMode" = "DepthOnly"}

            ZWrite On
            ColorMask 0
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }

        // This pass it not used during regular rendering, only for lightmap baking.
        Pass
        {
            Name "Meta"
            Tags{"LightMode" = "Meta"}

            Cull Off

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x

            #pragma vertex LightweightVertexMeta
            #pragma fragment LightweightFragmentMeta

            #pragma shader_feature _SPECULAR_SETUP
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICSPECGLOSSMAP
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            #pragma shader_feature _SPECGLOSSMAP

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitMetaPass.hlsl"

            ENDHLSL
        }
2 Likes

Ah, thank you! I just went and replaced the UsePass lines with the contents of the file so that I could just put the #define in the shader. Defining _ALPHATEST_ON for the depth only and shadow caster passes works. Thank you!