GameTech/URP 渲染实战
源码 / Ch03/C03_07_ShadowDepth.shader
Ch03/C03_07_ShadowDepth.shader
// 第 7 步:补上 ShadowCaster 和 DepthOnly 两个 Pass —— 能投射阴影,也能出现在深度图里。
Shader "Cookbook/Ch03/ShadowDepth"
{
    Properties
    {
        [MainTexture] _BaseMap ("Base Map", 2D) = "white" {}
        [MainColor]   _BaseColor ("Base Color", Color) = (1, 1, 1, 1)
        _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
        [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 2
    }

    SubShader
    {
        Tags { "RenderType"="TransparentCutout" "RenderPipeline"="UniversalPipeline" "Queue"="AlphaTest" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float4 _BaseMap_ST;
            half4  _BaseColor;
            half   _Cutoff;
        CBUFFER_END

        TEXTURE2D(_BaseMap);
        SAMPLER(sampler_BaseMap);

        // 三个 Pass 都要做同样的裁剪,抽成函数,保证“看到的形状 = 阴影形状 = 深度形状”
        void ClipByAlpha(float2 uv)
        {
            half a = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv).a * _BaseColor.a;
            clip(a - _Cutoff);
        }
        ENDHLSL

        // ---------------- 1. 颜色 Pass(与第 6 步相同) ----------------
        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForward" }
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; };
            struct Varyings   { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; float fogFactor : TEXCOORD1; };

            Varyings vert (Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                output.color = input.color;
                output.fogFactor = ComputeFogFactor(output.positionCS.z);
                return output;
            }

            half4 frag (Varyings input) : SV_Target
            {
                half4 tex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
                // 裁剪只看 贴图a × 颜色a,与 ClipByAlpha 完全一致;顶点色只影响 rgb
                clip(tex.a * _BaseColor.a - _Cutoff);
                half3 rgb = tex.rgb * _BaseColor.rgb * input.color.rgb;
                rgb = MixFog(rgb, input.fogFactor);
                return half4(rgb, 1);
            }
            ENDHLSL
        }

        // ---------------- 2. 阴影投射 Pass:把物体画进光源的 Shadow Map ----------------
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On
            ZTest LEqual
            ColorMask 0          // 只要深度,不写颜色
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex ShadowVert
            #pragma fragment ShadowFrag
            // 点光/聚光的阴影要用 _LightPosition 逐顶点求光方向,方向光用 _LightDirection
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            // Shadows.hlsl 用到了 CommonMaterial.hlsl 里的 LerpWhiteTo,单独引用时要先包含它
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

            // 由 URP 在渲染阴影前设置(ShadowUtils.SetupShadowCasterConstantBuffer)
            float3 _LightDirection;
            float3 _LightPosition;

            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; };
            struct Varyings   { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; };

            Varyings ShadowVert (Attributes input)
            {
                Varyings output;
                float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                float3 normalWS   = TransformObjectToWorldNormal(input.normalOS);
            #if defined(_CASTING_PUNCTUAL_LIGHT_SHADOW)
                float3 lightDirectionWS = normalize(_LightPosition - positionWS);
            #else
                float3 lightDirectionWS = _LightDirection;
            #endif
                // 沿光方向和法线方向各推一点,减轻自阴影条纹(Shadow Acne),细节见第 6 章
                positionWS = ApplyShadowBias(positionWS, normalWS, lightDirectionWS);
                output.positionCS = TransformWorldToHClip(positionWS);
                // 把超出近平面的投射体压回近平面,避免被裁掉导致阴影缺块
            #if UNITY_REVERSED_Z
                output.positionCS.z = min(output.positionCS.z, UNITY_NEAR_CLIP_VALUE);
            #else
                output.positionCS.z = max(output.positionCS.z, UNITY_NEAR_CLIP_VALUE);
            #endif
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return output;
            }

            half4 ShadowFrag (Varyings input) : SV_Target
            {
                ClipByAlpha(input.uv);   // 镂空的地方不投影
                return 0;
            }
            ENDHLSL
        }

        // ---------------- 3. 深度 Pass:深度预渲染(Depth Prepass)时把物体写进 _CameraDepthTexture ----------------
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On
            ColorMask R          // URP 自带 Shader 的写法;深度写进深度缓冲,颜色通道基本用不到
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex DepthVert
            #pragma fragment DepthFrag

            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; };
            struct Varyings   { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; };

            Varyings DepthVert (Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return output;
            }

            half DepthFrag (Varyings input) : SV_Target
            {
                ClipByAlpha(input.uv);
                return input.positionCS.z;
            }
            ENDHLSL
        }
    }
}