GameTech/URP 渲染实战
源码 / Ch06/FalloffCompare.shader
Ch06/FalloffCompare.shader
// 案例 6.2 点光/聚光的衰减:URP 的平滑衰减 vs 纯平方反比 vs 线性衰减
Shader "Cookbook/Ch06/FalloffCompare"
{
    Properties
    {
        [MainTexture] _BaseMap("Base Map", 2D) = "white" {}
        [MainColor] _BaseColor("Base Color", Color) = (0.8, 0.8, 0.8, 1)
        [KeywordEnum(URP, InverseSquare, Linear)] _Falloff("Falloff", Float) = 0
        _AmbientStrength("Ambient Strength", Range(0, 1)) = 0.1
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float4 _BaseMap_ST;
            half4  _BaseColor;
            half   _AmbientStrength;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForward" }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _ADDITIONAL_LIGHTS
            #pragma multi_compile _ _FORWARD_PLUS
            #pragma shader_feature_local_fragment _FALLOFF_URP _FALLOFF_INVERSESQUARE _FALLOFF_LINEAR
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS   : NORMAL;
                float2 uv         : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv         : TEXCOORD0;
                float3 positionWS : TEXCOORD1;
                float3 normalWS   : TEXCOORD2;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;
                VertexPositionInputs pos = GetVertexPositionInputs(input.positionOS.xyz);
                output.positionCS = pos.positionCS;
                output.positionWS = pos.positionWS;
                output.normalWS = TransformObjectToWorldNormal(input.normalOS);
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return output;
            }

            // 不用 GetAdditionalLight(),直接读 URP 上传的光源数组,自己算衰减。
            // URP 14 在所有平台都走常量数组(USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA 恒为 0)
            half3 PunctualLight(uint loopIndex, float3 positionWS, half3 normalWS)
            {
            #if USE_FORWARD_PLUS
                int i = int(loopIndex);                       // Forward+:循环变量就是全局光源下标
            #else
                int i = GetPerObjectLightIndex(loopIndex);    // Forward:先查“本物体的第几盏灯”对应的全局下标
            #endif
                float4 lightPos   = _AdditionalLightsPosition[i];     // w = 0 表示方向光
                half3  color      = _AdditionalLightsColor[i].rgb;
                half4  attenParam = _AdditionalLightsAttenuation[i];  // x = 1/range²,zw = 聚光角度参数
                half4  spotDir    = _AdditionalLightsSpotDir[i];

                float3 lightVector = lightPos.xyz - positionWS * lightPos.w;
                float  d2 = max(dot(lightVector, lightVector), HALF_MIN);
                half3  L = half3(lightVector * rsqrt(d2));

            #if defined(_FALLOFF_INVERSESQUARE)
                float atten = rcp(d2);                                         // 物理上的平方反比,没有窗口
            #elif defined(_FALLOFF_LINEAR)
                float atten = saturate(1.0 - sqrt(d2 * attenParam.x));         // 1 - d/range
            #else
                float atten = DistanceAttenuation(d2, attenParam.xy);          // URP:平方反比 × 平滑窗口
            #endif
                atten *= AngleAttenuation(spotDir.xyz, L, attenParam.zw);      // 聚光锥;点光的参数让它恒为 1

                return color * atten * saturate(dot(normalWS, L));
            }

            half4 frag(Varyings input) : SV_Target
            {
                half3 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).rgb * _BaseColor.rgb;
                half3 normalWS = normalize(input.normalWS);

                InputData inputData = (InputData)0;
                inputData.positionWS = input.positionWS;
                inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);

                Light mainLight = GetMainLight();
                half3 lighting = mainLight.color * mainLight.distanceAttenuation * saturate(dot(normalWS, mainLight.direction));

            #if defined(_ADDITIONAL_LIGHTS)
                uint lightCount = GetAdditionalLightsCount();
                #if USE_FORWARD_PLUS
                for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
                    lighting += PunctualLight(lightIndex, inputData.positionWS, normalWS);
                #endif
                LIGHT_LOOP_BEGIN(lightCount)
                    lighting += PunctualLight(lightIndex, inputData.positionWS, normalWS);
                LIGHT_LOOP_END
            #endif

                half3 color = albedo * lighting + albedo * SampleSH(normalWS) * _AmbientStrength;
                return half4(color, 1);
            }
            ENDHLSL
        }

        // ---- 投影与深度:见案例 6.4 与 6.8 ----
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On
            ZTest LEqual
            ColorMask 0

            HLSLPROGRAM
            #pragma vertex ShadowCasterVertex
            #pragma fragment ShadowCasterFragment
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            #include "Assets/Cookbook/Ch06/ShadowCasterPass.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On
            ColorMask R

            HLSLPROGRAM
            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment
            #include "Assets/Cookbook/Ch06/DepthOnlyPass.hlsl"
            ENDHLSL
        }
    }
}