GameTech/URP 渲染实战
源码 / shaders/Ch07/DerivativeTBN.glsl
shaders/Ch07/DerivativeTBN.glsl
// 网格没有切线数据时,用屏幕导数在像素里现算切线框架(per-pixel tangent frame)。(GLSL 版,对应 Ch07/DerivativeTBN.shader)
Shader "Cookbook/Ch07/DerivativeTBN"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

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

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

            uniform sampler2D _BaseMap;
            uniform sampler2D _BumpMap;

            VARYING vec2 uv;
            VARYING vec3 positionWS;
            VARYING vec3 normalWS;
            VARYING vec4 tangentWS;

            #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;
                tangentWS = vec4(nrm.tangentWS, tangentOS.w * GetOddNegativeScale());
            }
            #endif

            #ifdef FRAGMENT
            // 由 dp = T·du + B·dv 在屏幕 x、y 两个方向各写一次,解出 T = ∂p/∂u、B = ∂p/∂v。
            // 乘 sign(det) 保证结果与屏幕 y 轴朝向(平台差异)无关,并自动处理镜像 UV。
            // (WebGL 的 dFdy 朝上、D3D 的 ddy 朝下,正好靠这个 sign 抵消)
            mat3 DerivativeTangentFrame(vec3 N, vec3 positionWS, vec2 uv)
            {
                vec3 dp1 = dFdx(positionWS), dp2 = dFdy(positionWS);
                vec2 duv1 = dFdx(uv),        duv2 = dFdy(uv);
                float det = duv1.x * duv2.y - duv1.y * duv2.x;
                float s = det < 0.0 ? -1.0 : 1.0;
                vec3 T = (dp1 * duv2.y - dp2 * duv1.y) * s;
                vec3 B = (dp2 * duv1.x - dp1 * duv2.x) * s;
                // Gram-Schmidt:去掉与插值法线平行的分量,避免三角形之间出现折痕
                T = normalize(T - N * dot(N, T));
                B = normalize(B - N * dot(N, B));
                return mat3(T, B, N);   // GLSL 按列放:列 = T、B、N(HLSL 的 half3x3(T, B, N) 按行放)
            }

            void main()
            {
                vec3 N = normalize(normalWS);
                vec3 normalTS = UnpackNormalScale(texture(_BumpMap, uv), _BumpScale);
            #if defined(_DERIVATIVE_TBN)
                mat3 tbn = DerivativeTangentFrame(N, positionWS, uv);
            #else
                vec3 bitangent = tangentWS.w * cross(normalWS, tangentWS.xyz);
                mat3 tbn = mat3(tangentWS.xyz, bitangent, normalWS);
            #endif
                vec3 nWS = NormalizeNormalPerPixel(TransformTangentToWorld(normalTS, tbn));
                vec3 albedo = texture(_BaseMap, uv).rgb * _BaseColor.rgb;
                fragColor = vec4(Ch07Shade(positionWS, nWS, albedo, 0.45, 0.15, 1.0), 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
        }
    }
}