GameTech/URP 渲染实战
源码 / shaders/Ch16/DitherFade.glsl
shaders/Ch16/DitherFade.glsl
// 抖动透明(Dither / Screen-door Transparency):不混合,而是按屏幕位置的阈值图案丢弃一部分像素。(GLSL 版)
// 物体仍然是不透明队列、写深度、不需要排序;代价是近看能看到网点。阴影 Pass 用同样的抖动。
Shader "Cookbook/Ch16/DitherFade"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="AlphaTest" }

        GLSLINCLUDE
        #include "Ch16/Ch16Common.glsl"
        uniform vec4 _BaseColor;
        uniform float _Fade;
        uniform float _DitherShadow;

        // 4×4 Bayer 矩阵:16 个阈值均匀分布在 0..1,且相邻像素的阈值尽量“错开”
        float Bayer4x4(uvec2 p)
        {
            const float m[16] = float[16](
                 0.0,  8.0,  2.0, 10.0,
                12.0,  4.0, 14.0,  6.0,
                 3.0, 11.0,  1.0,  9.0,
                15.0,  7.0, 13.0,  5.0);
            p %= 4u;
            return (m[p.y * 4u + p.x] + 0.5) / 16.0;
        }

        #ifdef FRAGMENT
        // opacity 为 0.5 时,正好有一半像素的阈值低于它而被保留
        void DitherClip(vec2 pixelPos, float opacity)
        {
            if (opacity - Bayer4x4(uvec2(pixelPos)) < 0.0) discard;
        }
        #endif
        ENDGLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { Ch16Vert(); }
            #endif
            #ifdef FRAGMENT
            void main()
            {
                DitherClip(gl_FragCoord.xy, _Fade);     // gl_FragCoord.xy 在片元里就是像素坐标(对应 HLSL 的 SV_POSITION.xy)
                fragColor = vec4(SimpleLighting(_BaseColor.rgb, normalize(normalWS), positionWS, 0.5), 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { gl_Position = Ch16ShadowPositionCS(positionOS.xyz, normalOS); }
            #endif
            #ifdef FRAGMENT
            void main()
            {
                // 阴影贴图里的“像素”是阴影贴图的纹素,抖动后经过 PCF 软阴影过滤,会得到浅一些的阴影
                if (_DitherShadow > 0.5) DitherClip(gl_FragCoord.xy, _Fade);
                fragColor = vec4(0.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { gl_Position = TransformObjectToHClip(positionOS.xyz); }
            #endif
            #ifdef FRAGMENT
            void main() { DitherClip(gl_FragCoord.xy, _Fade); fragColor = vec4(gl_FragCoord.z); }
            #endif
            ENDGLSL
        }
    }
}