GameTech/URP 渲染实战
源码 / Ch08/AmbientSH.shader
Ch08/AmbientSH.shader
// 只显示球谐环境光(间接漫反射),可以选择只用 L0、L0+L1、或完整的 L0+L1+L2。
Shader "Cookbook/Ch08/AmbientSH"
{
    Properties
    {
        [MainColor] _BaseColor("Albedo", Color) = (0.8, 0.8, 0.8, 1)
        [Enum(L0,0,L0L1,1,L0L1L2,2)] _Bands("Bands", Float) = 2
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        CBUFFER_START(UnityPerMaterial)
            half4 _BaseColor;
            half _Bands;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

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

            Varyings vert(Attributes input)
            {
                Varyings o;
                o.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                o.normalWS = TransformObjectToWorldNormal(input.normalOS);
                return o;
            }

            // unity_SHAr/Ag/Ab:xyz = L1 三个系数,w = L0 系数“减去”L2 里 Y20 的常数部分;
            // unity_SHBr/Bg/Bb:L2 中 xy、yz、zz、zx 四项;unity_SHC:L2 中 x²-y² 一项(rgb 三通道)。
            // 这些系数已经乘上了“余弦卷积”因子并除以 π,所以直接算出来的就是 URP 约定下的间接漫反射。
            half3 EvaluateSHBands(half3 N, int bands)
            {
                // L2 在整个球面上积分为 0,但 zz 项的平均值是 1/3。Unity 把 Y20 的常数部分挪进了 w,
                // 想要“纯 L0”,就把 zz 项的平均值 SHB.z/3 加回去。
                half3 L0 = half3(unity_SHAr.w + unity_SHBr.z / 3.0,
                                 unity_SHAg.w + unity_SHBg.z / 3.0,
                                 unity_SHAb.w + unity_SHBb.z / 3.0);
                if (bands == 0) return L0;

                half3 L1 = half3(dot(unity_SHAr.xyz, N), dot(unity_SHAg.xyz, N), dot(unity_SHAb.xyz, N));
                if (bands == 1) return max(0, L0 + L1);

                // 完整三阶:与 URP 的 SampleSH(N) 完全相同(SHEvalLinearL0L1 + SHEvalLinearL2)
                return SampleSH(N);
            }

            half4 frag(Varyings i) : SV_Target
            {
                half3 N = normalize(i.normalWS);
                half3 irradiance = EvaluateSHBands(N, (int)round(_Bands));
                return half4(irradiance * _BaseColor.rgb, 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex v
            #pragma fragment f
            float4 v(float4 p : POSITION) : SV_POSITION { return TransformObjectToHClip(p.xyz); }
            half4 f() : SV_Target { return 0; }
            ENDHLSL
        }
    }
}