GameTech/URP 渲染实战
源码 / shaders/Ch24/SGToonEquivalent.glsl
shaders/Ch24/SGToonEquivalent.glsl
// 第 24 章:与“Unlit Graph + Custom Function 卡通光照”逐节点等价的手写 Shader(GLSL 版)。
// 用途有两个:
//   1) 让 Custom Function 用到的 .hlsl 在普通 Shader 里真实编译一遍(_float 和 _half 两套都调用到);
//   2) 渲染书中的效果图(Shader Graph 资源无法在批处理里手写生成)。
// SHADERGRAPH_PREVIEW 在这里被声明成一个本地关键字,只是为了把“预览分支”也编译、也能拍出来看,
// 真正的 Shader Graph 不需要也不应该这样做。
Shader "Cookbook/Ch24/SGToonEquivalent"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        GLSLINCLUDE
        uniform vec4  _BaseColor;
        uniform vec4  _ShadowTint;
        uniform float _Steps;
        uniform float _Smooth;
        uniform vec4  _RimColor;
        uniform float _RimPower;
        uniform float _UseHalf;
        uniform float _AddLights;
        ENDGLSL

        Pass
        {
            Name "Universal Forward"
            Tags { "LightMode"="UniversalForward" }
            GLSLPROGRAM
            // —— 在 Unlit Graph 的 Blackboard 里要手动加的 Keyword(_MAIN_LIGHT_SHADOWS 等)网页引擎始终打开 ——
            #pragma shader_feature_local _ SHADERGRAPH_PREVIEW

            #include "Ch24/CustomLighting.glsl"
            #include "Ch24/StylizedFunctions.glsl"

            VARYING vec3 positionWS;
            VARYING vec3 normalWS;

            #ifdef VERTEX
            void main()
            {
                VertexPositionInputs p = GetVertexPositionInputs(positionOS.xyz);
                gl_Position = p.positionCS; positionWS = p.positionWS;
                normalWS = TransformObjectToWorldNormal(normalOS);
            }
            #endif

            #ifdef FRAGMENT
            vec4 frag()
            {
                // [Position 节点 World] [Normal Vector 节点 World] [View Direction 节点 World] [Screen Position 节点 Default]
                vec3 posWS = positionWS;
                vec3 nWS = normalize(normalWS);
                vec3 viewWS = GetWorldSpaceNormalizeViewDir(posWS);
                vec2 screenUV = GetNormalizedScreenSpaceUV(gl_FragCoord);

                // [Custom Function: MainLight]
                vec3 lightDir, lightColor; float distAtten, shadowAtten;
                if (_UseHalf > 0.5) MainLight_half(posWS, lightDir, lightColor, distAtten, shadowAtten);
                else MainLight_float(posWS, lightDir, lightColor, distAtten, shadowAtten);

                // [Dot Product] → [Custom Function: ToonRamp]
                float NdotL = dot(nWS, lightDir);
                float ramp;
                if (_UseHalf > 0.5) ToonRamp_half(NdotL, shadowAtten, _Steps, _Smooth, ramp);
                else ToonRamp_float(NdotL, shadowAtten, _Steps, _Smooth, ramp);

                // [Lerp] 阴影色 ↔ 固有色,再乘主光颜色
                vec3 albedo = _BaseColor.rgb;
                vec3 toon = mix(albedo * _ShadowTint.rgb, albedo, ramp) * lightColor;

                // [Custom Function: RimLight] 只在受光侧出现
                float rim;
                RimLight_float(nWS, viewWS, lightDir, _RimPower, 1.0, rim);
                rim *= step(0.5, ramp);   // 阴影里不出边缘光

                // [Custom Function: AdditionalLightsLambert] 点光、聚光
                vec3 extra = vec3(0.0);
                if (_AddLights > 0.5)
                {
                    AdditionalLightsLambert_float(posWS, nWS, screenUV, extra);
                    vec3 extraHalf; AdditionalLightsLambert_half(posWS, nWS, screenUV, extraHalf);
                    extra = _UseHalf > 0.5 ? extraHalf : extra;
                }

                // [Ambient 节点(Equator/Sky)] 这里用等价的 SampleSH
            #if defined(SHADERGRAPH_PREVIEW)
                vec3 ambient = vec3(0.2);
            #else
                vec3 ambient = SampleSH(nWS);
            #endif

                vec3 color = toon + albedo * (ambient * 0.6 + extra) + rim * _RimColor.rgb;
                return vec4(color, 1);   // → Master Stack 的 Base Color
            }
            void main() { fragColor = frag(); }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            GLSLPROGRAM
            #define CH23_SHADOW_PASS
            #include "Ch23/Ch23Common.glsl"
            ENDGLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #define CH23_DEPTH_PASS
            #include "Ch23/Ch23Common.glsl"
            ENDGLSL
        }
    }
}