GameTech/URP 渲染实战
源码 / shaders/Ch22/MeshDecal.glsl
shaders/Ch22/MeshDecal.glsl
// 案例 22.8–22.9:网格贴花(Mesh Decal)——贴在表面上的一块真实网格,用深度偏移(Offset)避免 Z-Fighting(GLSL 版)
Shader "Cookbook/Ch22/MeshDecal"
{
    SubShader
    {
        Tags { "RenderType"="Transparent" "RenderPipeline"="UniversalPipeline" "Queue"="Transparent-100" "IgnoreProjector"="True" }

        Pass
        {
            Name "MeshDecal"
            Tags { "LightMode"="UniversalForward" }
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            ZTest LEqual
            // 深度偏移:depth += factor × 斜率 + units × 最小可分辨深度单位
            // 负值 = 往相机方向拉一点,让与底面共面的贴花稳定地赢下深度测试
            Offset [_OffsetFactor], [_OffsetUnits]

            GLSLPROGRAM
            #pragma shader_feature_local_fragment _NORMALMAP

            uniform vec4 _BaseMap_ST;
            uniform vec4 _BaseColor;
            uniform float _BumpScale, _Smoothness;
            uniform float _OffsetFactor, _OffsetUnits;
            uniform float _DebugColor;
            uniform sampler2D _BaseMap;
            uniform sampler2D _BumpMap;

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

            #ifdef VERTEX
            void main()
            {
                VertexPositionInputs p = GetVertexPositionInputs(positionOS.xyz);
                VertexNormalInputs n = GetVertexNormalInputs(normalOS, tangentOS);
                gl_Position = p.positionCS;
                positionWS = p.positionWS;
                normalWS = n.normalWS;
                tangentWS = vec4(n.tangentWS, tangentOS.w * GetOddNegativeScale());
                uv = TransformTex(texcoord0.xy, _BaseMap_ST);
            }
            #endif

            #ifdef FRAGMENT
            vec4 frag()
            {
                vec4 decal = texture(_BaseMap, uv) * _BaseColor;
                if (_DebugColor > 0.5) return vec4(0.1, 0.8, 1.0, 0.55);   // 调试:显示生成的网格覆盖范围

                vec3 n = normalize(normalWS);
            #if defined(_NORMALMAP)
                vec3 nTS = UnpackNormalScale(texture(_BumpMap, uv), _BumpScale);
                vec3 bitangent = tangentWS.w * cross(normalWS, tangentWS.xyz);
                n = normalize(TransformTangentToWorld(nTS, mat3(tangentWS.xyz, bitangent, normalWS)));
            #endif
                InputData inputData = InitInputData();
                inputData.positionWS = positionWS;
                inputData.normalWS = n;
                inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(positionWS);
                inputData.shadowCoord = TransformWorldToShadowCoord(positionWS);
                inputData.bakedGI = SampleSH(n);
                inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(gl_FragCoord);
                inputData.shadowMask = vec4(1, 1, 1, 1);
                SurfaceData s = InitSurfaceData();
                s.albedo = decal.rgb;
                s.metallic = 0.0;
                s.smoothness = _Smoothness;
                s.occlusion = 1.0;
                s.alpha = decal.a;
                vec3 color = UniversalFragmentPBR(inputData, s).rgb;
                return vec4(color, decal.a);
            }
            void main() { fragColor = frag(); }
            #endif
            ENDGLSL
        }
    }
}