GameTech/URP 渲染实战
源码 / Ch26/MyChromaticAberration.shader
Ch26/MyChromaticAberration.shader
// 案例 26.6:色差。沿径向把 R、G、B 采样到不同位置;采样数 > 3 时用“光谱权重”做平滑色散。
Shader "Cookbook/Ch26/MyChromaticAberration"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
        ZTest Always ZWrite Off Cull Off
        Pass
        {
            Name "ChromaticAberration"
            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"

            float4 _CAParams;   // x 强度, y 采样数

            // t ∈ [0,1] 映射到 红→绿→蓝 的权重(三段帐篷函数),所有样本的权重之和按通道归一化
            half3 SpectrumWeight(float t)
            {
                half r = saturate(1.0 - abs(t * 2.0));
                half g = saturate(1.0 - abs(t * 2.0 - 1.0));
                half b = saturate(1.0 - abs(t * 2.0 - 2.0));
                return half3(r, g, b);
            }

            half4 Frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                float2 uv = input.texcoord;
                float2 d = uv - 0.5;
                // 偏移随半径平方增长:中心几乎无色差,边缘最强(和真实镜头的横向色差一致)
                float2 offset = d * dot(d, d) * _CAParams.x * 0.15;

                int n = (int)_CAParams.y;
                half3 sum = 0, wsum = 0;
                [loop] for (int i = 0; i < n; i++)
                {
                    float t = i / (float)(n - 1);               // 0 = 红(向外)… 1 = 蓝(向内)
                    half3 w = SpectrumWeight(t);
                    float2 suv = uv + offset * (0.5 - t) * 2.0;
                    sum += SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, suv).rgb * w;
                    wsum += w;
                }
                return half4(sum / max(wsum, 1e-4), 1.0);
            }
            ENDHLSL
        }
    }
}