SHADERPASS_SHADOWCASTER and testing for the shadowcaster pass

Hi,

In URP 8.2.x, I used SHADERPASS_SHADOWCASTER to change the behavior of some custom nodes in shadergraph during shadowcasting passes:

#ifdef SHADERPASS_SHADOWCASTER
// do something while shadowcasting...
#else
// do something normally...
#endif

I’ve upgraded to 2020.2 and URP 10.2.2 and this no longer works. I can’t seem to find any reference in the changelog or docs about this functionality. I did see on the (now retired) git that some references were made to ‘Fix SHADERPASS inconsistency between pipelines’, see eg [Skip CI] Shader Graph Targets (#4940) · Unity-Technologies/Graphics@db4dddf · GitHub

This looks like changes were made to the format:

#if (SHADERPASS == SHADERPASS_SHADOWCASTER)
// stuff
#endif

However, I can’t seem to get this to work in my shader! Whatever I try the value is always true. What is the ‘correct’ way to test for the shadowcasting pass in URP 10?

Cheers,
Elliot

1 Like

More information:

I’ve looked at the ShaderLab code generated by ShaderGraph for each version.

In the URP 8.2 version, the shadowcaster pass looks like the following:

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

            // Render State
            Blend One Zero, One Zero
            Cull Off
            ZTest LEqual
            ZWrite On
            // ColorMask: <None>

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
       
            // Debug
            // <None>
       
            // --------------------------------------------------
            // Pass
       
            // Pragmas
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0
            #pragma multi_compile_instancing

 ....(a whole forest of keyword permutations, followed by)...

            #if defined(UNITY_SUPPORT_INSTANCING) && defined(INSTANCING_ON)
            #define UNITY_DOTS_INSTANCING_ENABLED
            #endif
            #define SHADERPASS_SHADOWCASTER
            //-------------------------------------------------------------------------------------
            // Dots Instancing vars
            //-------------------------------------------------------------------------------------
....

Hence, SHADERPASS_SHADOWCASTER is clearly defined in the Shadowcasting pass.

In URP 10.2 the generated shader is different:

            Pass
            {
                Name "ShadowCaster"
                Tags
                {
                    "LightMode" = "ShadowCaster"
                }
   
                // Render State
                Cull Off
                Blend One Zero
                ZTest LEqual
                ZWrite On
                ColorMask 0
   
                // Debug
                // <None>
   
                // --------------------------------------------------
                // Pass
   
                HLSLPROGRAM
   
                // Pragmas
                #pragma target 4.5
                #pragma exclude_renderers gles gles3 glcore
                #pragma multi_compile_instancing
                #pragma multi_compile _ DOTS_INSTANCING_ON
                #pragma vertex vert
                #pragma fragment frag
   
                #if SHADER_TARGET >= 35 && (defined(SHADER_API_D3D11) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_PSSL) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL))
                    #define UNITY_SUPPORT_INSTANCING
                #endif
                #if defined(UNITY_SUPPORT_INSTANCING) && defined(INSTANCING_ON)
                    #define UNITY_HYBRID_V1_INSTANCING_ENABLED
                #endif
                #if defined(UNITY_HYBRID_V1_INSTANCING_ENABLED)
                #define HYBRID_V1_CUSTOM_ADDITIONAL_MATERIAL_VARS \
                UNITY_DEFINE_INSTANCED_PROP(float, _PixelSize_Array)\
                UNITY_DEFINE_INSTANCED_PROP(float4, _PixelGridOrigin_Array)
                #define _PixelSize UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, _PixelSize_Array)
                #define _PixelGridOrigin UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, _PixelGridOrigin_Array)
                #endif
   
                // Keywords
                // PassKeywords: <None>
                #pragma shader_feature_local _ COLOR_GRADING_ON
                #pragma shader_feature_local _ RECEIVE_SHADOWS_ON
                #pragma shader_feature_local _ USE_OBJECT_POSITION_ON
                #pragma shader_feature_local _ NORMAL_MAP_ON
                #pragma shader_feature_local _ USE_EMISSION_ON
                #pragma shader_feature_local _ USE_ALPHA_ON
               
...(a whole forest of keyword permutations, followed by)... 
   
                #define FEATURES_GRAPH_VERTEX
                /* WARNING: $splice Could not find named fragment 'PassInstancing' */
                #define SHADERPASS SHADERPASS_SHADOWCASTER
                /* WARNING: $splice Could not find named fragment 'DotsInstancingVars' */
   
                // Includes
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"
   ...

So the #define SHADERPASS_SHADOWCASTER has been swapped for #define SHADERPASS SHADERPASS_SHADOWCASTER, and I would expect the if statement in the first post to now work:

#if (SHADERPASS == SHADERPASS_SHADOWCASTER)

But it still doesn’t! The SHADERPASS_SHADOWCASTER statement is defined in Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl. If I put the following into a ShaderGraph Custom Node…

#ifndef UNIVERSAL_SHADERPASS_INCLUDED
#error "Universal ShaderPass not included."
#endif

…then I receive the error ‘Universal ShaderPass not included’ (the same works if I test whether SHADERPASS_SHADOWCASTER is defined).

The 10.2 generated ShadowCaster Pass hlsl ends with:

                // --------------------------------------------------
                // Main
   
                #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/Varyings.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShadowCasterPass.hlsl"
   
                ENDHLSL
            }

…which does include the ShaderPass.hlsl file.

No answers yet, but I’ll keep digging.

1 Like

Ok, so I can get this to work if I manually include the ShaderPass.hlsl file at the start of my custom function

Full Solution in 10.2:

Shadows.hlsl:

#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"

inline void shadowcaster_float(out float _out) {
#if ( SHADERPASS == SHADERPASS_SHADOWCASTER )
    _out = 1.0;
#else
    _out = 0.0;
#endif
}

This will produce an object which is transparent yet still casts a shadow:
6689842--767404--upload_2021-1-5_13-21-17.png

If you do not include the ShaderPass.hlsl file in the custom function, it does not work! So it seems to me that this is a bug, and that the ShaderPass include statement should occur higher up in the generated ShaderGraph file. (Perhaps this should be moved to the ShaderGraph forum?)

1 Like

Note that if you want backwards compatibility with previous render pipeline versions, you will need to switch based on the SRP version. There is also a bug there, so you will need to include Version.hlsl otherwise the VERSION_GREATER_EQUAL macro will not be defined in URP 10.2.

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl"
#if VERSION_GREATER_EQUAL(10, 0)
#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"
#endif

... then in your function

#if VERSION_GREATER_EQUAL(10,0)
#define SHADOWTEST ( SHADERPASS == SHADERPASS_SHADOWCASTER )
#else
#define SHADOWTEST defined(SHADERPASS_SHADOWCASTER)
#endif

#if SHADOWTEST
    //do something in shadowcaster pass
#else
    //do something in normal pass
#endif
4 Likes