GameTech/URP 渲染实战
源码 / shaders/Ch08/BoxProjection.glsl
shaders/Ch08/BoxProjection.glsl
// 盒体投影(Box Projection)演示:用 URP 的 BoxProjectedCubemapDirection 修正反射方向。(GLSL 版,对应 Ch08/BoxProjection.shader)
// 为了在批处理拍摄中完全可控,探针参数(cubemap、位置、盒体)由材质直接提供;
// 场景里真正的反射探针走的是 GlossyEnvironmentReflection 里同一个函数,原理完全相同。
Shader "Cookbook/Ch08/BoxProjection"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }
        GLSLINCLUDE
        uniform vec4  _ProbePosition;
        uniform vec4  _BoxMin;
        uniform vec4  _BoxMax;
        uniform vec4  _BaseColor;
        uniform float _Reflectivity;
        uniform float _Roughness;
        ENDGLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            #pragma shader_feature_local_fragment _BOX_PROJECTION

            uniform samplerCube _Cube;

            VARYING vec3 positionWS;
            VARYING vec3 normalWS;

            #ifdef VERTEX
            void main()
            {
                positionWS = TransformObjectToWorld(positionOS.xyz);
                gl_Position = TransformWorldToHClip(positionWS);
                normalWS = TransformObjectToWorldNormal(normalOS);
            }
            #endif

            #ifdef FRAGMENT
            // URP ImageBasedLighting.hlsl 里的 BoxProjectedCubemapDirection(网页版公共库没有这个函数,照原样抄在这里):
            // 把反射射线和探针的 AABB 求交,用“交点相对探针中心的方向”去查 cubemap
            vec3 BoxProjectedCubemapDirection(vec3 reflectionWS, vec3 positionWS, vec4 cubemapPositionWS, vec4 boxMin, vec4 boxMax)
            {
                if (cubemapPositionWS.w > 0.0)
                {
                    vec3 boxMinMax = mix(boxMin.xyz, boxMax.xyz, vec3(greaterThan(reflectionWS, vec3(0.0))));
                    vec3 rbMinMax = (boxMinMax - positionWS) / reflectionWS;
                    float fa = min(min(rbMinMax.x, rbMinMax.y), rbMinMax.z);
                    vec3 worldPos = positionWS - cubemapPositionWS.xyz;
                    return worldPos + reflectionWS * fa;
                }
                return reflectionWS;
            }

            void main()
            {
                vec3 N = normalize(normalWS);
                vec3 V = GetWorldSpaceNormalizeViewDir(positionWS);
                vec3 R = reflect(-V, N);
            #if defined(_BOX_PROJECTION)
                // 第 4 个分量 w > 0 表示“这个探针启用了盒体投影”,与 URP 的 unity_SpecCube0_ProbePosition 约定一致
                R = BoxProjectedCubemapDirection(R, positionWS, vec4(_ProbePosition.xyz, 1.0), _BoxMin, _BoxMax);
            #endif
                float mip = PerceptualRoughnessToMipmapLevel(_Roughness);
                vec3 refl = textureLod(_Cube, R, mip).rgb;

                // 简化的菲涅尔:掠射角反射更强
                float NoV = saturate(dot(N, V));
                float F = _Reflectivity + (1.0 - _Reflectivity) * Pow4(1.0 - NoV) * 0.5;
                vec3 diffuse = _BaseColor.rgb * SampleSH(N);
                fragColor = vec4(mix(diffuse, refl, F), 1.0);
            }
            #endif
            ENDGLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #include "Ch08/Ch08Passes.glsl"
            #ifdef VERTEX
            void main() { gl_Position = Ch08DepthVert(); }
            #endif
            #ifdef FRAGMENT
            void main() { fragColor = Ch08NullFrag(); }
            #endif
            ENDGLSL
        }
    }
}