GameTech/URP 渲染实战
源码 / Ch28/ReconstructWorldPos.shader
Ch28/ReconstructWorldPos.shader
// 案例 28.1:从深度重建世界坐标的两种方法,并把结果可视化。
// Pass 0:逆 VP 矩阵法,输出 frac(positionWS);Pass 1:视锥射线法,在原画面上叠加 1 米世界网格;
// Pass 2:两种方法的差值放大显示(用来验证两者一致)。
Shader "Cookbook/Ch28/ReconstructWorldPos"
{
    Properties
    {
        _GridColor ("Grid Color", Color) = (1, 0.45, 0.1, 1)
        _GridWidth ("Grid Width (px)", Range(0.5, 4)) = 1.2
        _DiffScale ("Diff Scale", Float) = 1000
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
        ZWrite Off ZTest Always Cull Off Blend Off

        HLSLINCLUDE
        #include "Assets/Cookbook/Ch28/ScreenSpaceCommon.hlsl"

        CBUFFER_START(UnityPerMaterial)
            half4 _GridColor;
            float _GridWidth;
            float _DiffScale;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "InvVP"
            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            half4 Frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                float2 uv = input.texcoord;
                half4 sceneColor = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, uv);
                float depth = SampleDeviceDepth(uv);
                if (IsSkyDepth(depth)) return sceneColor * 0.35;   // 天空没有几何,压暗区分
                float3 positionWS = ReconstructPositionWS(uv, depth);
                // +0.5:避开地面 y=0 这种整数坐标,frac 在 0/1 之间来回跳会产生噪点
                return half4(frac(positionWS + 0.5), 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "ViewRay"
            HLSLPROGRAM
            #pragma vertex VertRay
            #pragma fragment Frag
            half4 Frag(VaryingsRay input) : SV_Target
            {
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                float2 uv = input.texcoord;
                half4 sceneColor = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, uv);
                float rawDepth = SampleSceneDepth(uv);
                if (IsSkyDepth(rawDepth)) return sceneColor;
                float3 positionWS = ReconstructPositionWSFromRay(input.farRayWS, rawDepth);

                // 1 米网格线:用 fwidth 把线宽换算成像素,远近粗细一致
                float3 dist = abs(frac(positionWS + 0.5) - 0.5);
                float3 width = fwidth(positionWS) * _GridWidth;
                float3 lines = 1 - smoothstep(width * 0.5, width * 1.5, dist);
                float grid = saturate(max(lines.x, max(lines.y, lines.z)));
                return lerp(sceneColor, _GridColor, grid * 0.85);
            }
            ENDHLSL
        }

        Pass
        {
            Name "Diff"
            HLSLPROGRAM
            #pragma vertex VertRay
            #pragma fragment Frag
            half4 Frag(VaryingsRay input) : SV_Target
            {
                float2 uv = input.texcoord;
                float rawDepth = SampleSceneDepth(uv);
                float3 a = ReconstructPositionWS(uv, SampleDeviceDepth(uv));
                float3 b = ReconstructPositionWSFromRay(input.farRayWS, rawDepth);
                return half4(saturate(abs(a - b) * _DiffScale), 1);
            }
            ENDHLSL
        }
    }
}