GameTech/URP 渲染实战
源码 / Ch08/BoxProjection.shader
Ch08/BoxProjection.shader
// 盒体投影(Box Projection)演示:用 URP 的 BoxProjectedCubemapDirection 修正反射方向。
// 为了在批处理拍摄中完全可控,探针参数(cubemap、位置、盒体)由材质直接提供;
// 场景里真正的反射探针走的是 GlossyEnvironmentReflection 里同一个函数,原理完全相同。
Shader "Cookbook/Ch08/BoxProjection"
{
    Properties
    {
        [NoScaleOffset] _Cube("Probe Cubemap", Cube) = "" {}
        _ProbePosition("Probe Position (xyz)", Vector) = (0, 1.5, 0, 1)
        _BoxMin("Box Min", Vector) = (-3, 0, -3, 0)
        _BoxMax("Box Max", Vector) = (3, 3, 3, 0)
        [Toggle(_BOX_PROJECTION)] _UseBox("Box Projection", Float) = 1
        _BaseColor("Base Color", Color) = (0.1, 0.1, 0.11, 1)
        _Reflectivity("Reflectivity", Range(0, 1)) = 0.6
        _Roughness("Perceptual Roughness", Range(0, 1)) = 0.05
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        CBUFFER_START(UnityPerMaterial)
            float4 _ProbePosition;
            float4 _BoxMin;
            float4 _BoxMax;
            half4 _BaseColor;
            half _Reflectivity;
            half _Roughness;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local_fragment _BOX_PROJECTION
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            TEXTURECUBE(_Cube); SAMPLER(sampler_Cube);

            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; };
            struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; float3 normalWS : TEXCOORD1; };

            Varyings vert(Attributes input)
            {
                Varyings o;
                o.positionWS = TransformObjectToWorld(input.positionOS.xyz);
                o.positionCS = TransformWorldToHClip(o.positionWS);
                o.normalWS = TransformObjectToWorldNormal(input.normalOS);
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                half3 N = normalize(i.normalWS);
                half3 V = GetWorldSpaceNormalizeViewDir(i.positionWS);
                half3 R = reflect(-V, N);
            #if defined(_BOX_PROJECTION)
                // 第 4 个分量 w > 0 表示“这个探针启用了盒体投影”,与 URP 的 unity_SpecCube0_ProbePosition 约定一致
                R = BoxProjectedCubemapDirection(R, i.positionWS, float4(_ProbePosition.xyz, 1), _BoxMin, _BoxMax);
            #endif
                half mip = PerceptualRoughnessToMipmapLevel(_Roughness);
                half3 refl = SAMPLE_TEXTURECUBE_LOD(_Cube, sampler_Cube, R, mip).rgb;

                // 简化的菲涅尔:掠射角反射更强
                half NoV = saturate(dot(N, V));
                half F = _Reflectivity + (1 - _Reflectivity) * Pow4(1 - NoV) * 0.5;
                half3 diffuse = _BaseColor.rgb * SampleSH(N);
                return half4(lerp(diffuse, refl, F), 1);
            }
            ENDHLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex Ch08DepthVert
            #pragma fragment Ch08NullFrag
            #include "Assets/Cookbook/Ch08/Ch08Passes.hlsl"
            ENDHLSL
        }
    }
}