GameTech/URP 渲染实战
源码 / Ch16/DitherFade.shader
Ch16/DitherFade.shader
// 抖动透明(Dither / Screen-door Transparency):不混合,而是按屏幕位置的阈值图案丢弃一部分像素。
// 物体仍然是不透明队列、写深度、不需要排序;代价是近看能看到网点。阴影 Pass 用同样的抖动。
Shader "Cookbook/Ch16/DitherFade"
{
    Properties
    {
        _BaseColor ("Color", Color) = (0.9, 0.5, 0.25, 1)
        _Fade ("Opacity", Range(0, 1)) = 0.5
        [Toggle] _DitherShadow ("Dither In Shadow Pass", Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="AlphaTest" }

        HLSLINCLUDE
        #include "Assets/Cookbook/Ch16/Ch16Common.hlsl"
        CBUFFER_START(UnityPerMaterial)
            half4 _BaseColor;
            half  _Fade;
            float _DitherShadow;
        CBUFFER_END

        // 4×4 Bayer 矩阵:16 个阈值均匀分布在 0..1,且相邻像素的阈值尽量“错开”
        float Bayer4x4(uint2 p)
        {
            const float m[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 %= 4;
            return (m[p.y * 4 + p.x] + 0.5) / 16.0;
        }

        // opacity 为 0.5 时,正好有一半像素的阈值低于它而被保留
        void DitherClip(float2 pixelPos, half opacity)
        {
            clip(opacity - Bayer4x4((uint2)pixelPos));
        }
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            HLSLPROGRAM
            #pragma vertex Ch16Vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
            half4 frag(Varyings i) : SV_Target
            {
                DitherClip(i.positionCS.xy, _Fade);     // SV_POSITION.xy 在片元里就是像素坐标
                return half4(SimpleLighting(_BaseColor.rgb, normalize(i.normalWS), i.positionWS, 0.5h), 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            float4 vert(Attributes v) : SV_POSITION { return Ch16ShadowPositionCS(v.positionOS.xyz, v.normalOS); }
            half4 frag(float4 positionCS : SV_POSITION) : SV_Target
            {
                // 阴影贴图里的“像素”是阴影贴图的纹素,抖动后经过 PCF 软阴影过滤,会得到浅一些的阴影
                if (_DitherShadow > 0.5) DitherClip(positionCS.xy, _Fade);
                return 0;
            }
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            float4 vert(Attributes v) : SV_POSITION { return TransformObjectToHClip(v.positionOS.xyz); }
            half4 frag(float4 positionCS : SV_POSITION) : SV_Target { DitherClip(positionCS.xy, _Fade); return 0; }
            ENDHLSL
        }
    }
}