GameTech/URP 渲染实战
源码 / shaders/Ch07/NormalMap.glsl
shaders/Ch07/NormalMap.glsl
// 标准的切线空间法线贴图:顶点里准备 TBN,片元里解码法线贴图并变换到世界空间。(GLSL 版,对应 Ch07/NormalMap.shader)
Shader "Cookbook/Ch07/NormalMap"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        GLSLINCLUDE
        uniform vec4  _BaseMap_ST;
        uniform vec4  _BaseColor;
        uniform float _BumpScale;
        uniform float _Gloss;
        uniform float _SpecStrength;
        ENDGLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            #pragma shader_feature_local _NORMALMAP
            #include "Ch07/Ch07Common.glsl"

            uniform sampler2D _BaseMap;
            uniform sampler2D _BumpMap;

            VARYING vec2 uv;
            VARYING vec3 positionWS;
            VARYING vec3 normalWS;
        #if defined(_NORMALMAP)
            VARYING vec4 tangentWS;   // xyz 切线,w 副切线符号
        #endif
            VARYING float fogFactor;

            #ifdef VERTEX
            void main()
            {
                VertexPositionInputs pos = GetVertexPositionInputs(positionOS.xyz);
                VertexNormalInputs nrm = GetVertexNormalInputs(normalOS, tangentOS);
                gl_Position = pos.positionCS;
                positionWS = pos.positionWS;
                uv = TransformTex(texcoord0.xy, _BaseMap_ST);
                normalWS = nrm.normalWS;
            #if defined(_NORMALMAP)
                // 与 URP Lit 相同:只传 T 和符号,B 在片元里用叉乘重建,省一个插值器
                float sign = tangentOS.w * GetOddNegativeScale();
                tangentWS = vec4(nrm.tangentWS, sign);
            #endif
                fogFactor = ComputeFogFactor(pos.positionCS.z);
            }
            #endif

            #ifdef FRAGMENT
            void main()
            {
                vec4 albedo = texture(_BaseMap, uv) * _BaseColor;

            #if defined(_NORMALMAP)
                // 1) 解码:Unity 的 UnpackNormalScale 自动处理 DXT5nm/BC5(AG/RG 两通道)与 RGB 三通道;网页版的法线贴图是 RGB
                vec3 normalTS = UnpackNormalScale(texture(_BumpMap, uv), _BumpScale);
                // 2) 重建副切线并组装 TBN。HLSL 的 half3x3(T, B, N) 是按行放,GLSL 的 mat3(T, B, N) 按列放,
                //    TransformTangentToWorld 已按各自的约定写好:结果都是 x*T + y*B + z*N
                float sgn = tangentWS.w;
                vec3 bitangent = sgn * cross(normalWS, tangentWS.xyz);
                mat3 tangentToWorld = mat3(tangentWS.xyz, bitangent, normalWS);
                // 3) 切线空间 → 世界空间
                vec3 nWS = TransformTangentToWorld(normalTS, tangentToWorld);
            #else
                vec3 nWS = normalWS;
            #endif
                nWS = NormalizeNormalPerPixel(nWS);

                vec3 color = Ch07Shade(positionWS, nWS, albedo.rgb, _Gloss, _SpecStrength, 1.0);
                color = MixFog(color, fogFactor);
                fragColor = vec4(color, 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            GLSLPROGRAM
            #define CH07_SHADOW_PASS
            #include "Ch07/Ch07Common.glsl"
            ENDGLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #define CH07_DEPTH_PASS
            #include "Ch07/Ch07Common.glsl"
            ENDGLSL
        }
    }
}