GameTech/URP 渲染实战
源码 / shaders/Ch08/AmbientSH.glsl
shaders/Ch08/AmbientSH.glsl
// 只显示球谐环境光(间接漫反射),可以选择只用 L0、L0+L1、或完整的 L0+L1+L2。(GLSL 版,对应 Ch08/AmbientSH.shader)
Shader "Cookbook/Ch08/AmbientSH"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        GLSLINCLUDE
        uniform vec4  _BaseColor;
        uniform float _Bands;
        ENDGLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            VARYING vec3 normalWS;

            #ifdef VERTEX
            void main()
            {
                gl_Position = TransformObjectToHClip(positionOS.xyz);
                normalWS = TransformObjectToWorldNormal(normalOS);
            }
            #endif

            #ifdef FRAGMENT
            // 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 约定下的间接漫反射。
            vec3 EvaluateSHBands(vec3 N, int bands)
            {
                // L2 在整个球面上积分为 0,但 zz 项的平均值是 1/3。Unity 把 Y20 的常数部分挪进了 w,
                // 想要“纯 L0”,就把 zz 项的平均值 SHB.z/3 加回去。
                vec3 L0 = vec3(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;

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

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

            void main()
            {
                vec3 N = normalize(normalWS);
                vec3 irradiance = EvaluateSHBands(N, int(round(_Bands)));
                fragColor = vec4(irradiance * _BaseColor.rgb, 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { gl_Position = TransformObjectToHClip(positionOS.xyz); }
            #endif
            #ifdef FRAGMENT
            void main() { fragColor = vec4(gl_FragCoord.z); }
            #endif
            ENDGLSL
        }
    }
}