Neilyodog Blog

半透明剔除Dither

Dither / Screen-Door Transparency

参考网站

文章配图

核心代码

  • Dither Noise

(% = fmod 取余)

void Unity_Dither_float(float4 ScreenPosition, out float Out)
{
    float2 uv = ScreenPosition.xy * _ScreenParams.xy;
    float DITHER_THRESHOLDS[16] =
    {
        1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
        13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
        4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
        16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
    };
    uint index = (uint(uv.x) % 4) * 4 + uint(uv.y) % 4;
    Out = 1 - DITHER_THRESHOLDS[index];
}
  • 将Noise映射到屏幕

float dither84 = Dither4x4Bayer( fmod(screenPosition.x, 4), fmod(screenPosition.y, 4) );

  • 需要一个深度

可以直接用LinearEyeDepth(需要深度图)或算物体与相机的距离

连连看

这边算的是物体和相机的距离,然后减去noise,结果用smoothstep进行微调

文章配图

Unilt shader(代码有点问题,连连看连反了)

Shader "ASESampleShaders/DitheringFade"
{
    Properties
    {
        _Distance ("有效距离", float) = 1
    }
    SubShader
    {
        Pass
        {
            Tags { "Queue" = "Geometry" "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }


            HLSLPROGRAM


            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x


            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog


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


            struct appdata
            {
                float4 positionOS: POSITION;
                float4 color: COLOR;
            };

            struct v2f
            {
                float4 positionHCS: SV_POSITION;
                float3 worldPos: TEXCOORD0;
            };


            CBUFFER_START(UnityPerMaterial)
            half _Distance;
            CBUFFER_END


            inline float Dither4x4Bayer(int x, int y)
            {
                const float dither[ 16 ] = {
                    1, 9, 3, 11,
                    13, 5, 15, 7,
                    4, 12, 2, 10,
                    16, 8, 14, 6
                };
                int r = y * 4 + x;
                return dither[r] / 16; // same # of instructions as pre-dividing due to compiler magic
            }

            v2f vert(appdata v)
            {
                v2f o;
                float4 clipPos = TransformObjectToHClip(v.positionOS);
                o.positionHCS = TransformObjectToHClip(v.positionOS);
                o.worldPos = mul(unity_ObjectToWorld, v.positionOS).xyz;
                return o;
            }

            float4 frag(v2f i): SV_Target
            {
                float3 posObj = TransformObjectToWorld(float3(0, 0, 0));
                float3 WorldPosition = i.worldPos;
                // float2 screenPos = i.positionHCS.xy;// / _ScreenParams.xy;
                // half4 HCS = screenPos / screenPos.w;
                // HCS.z = ( UNITY_NEAR_CLIP_VALUE >= 0 ) ? HCS.z : HCS.z * 0.5 + 0.5; //TODO:more cheap?
                // half2 clipScreen = screenPos.xy;// * _ScreenParams.xy;
                half dither = Dither4x4Bayer(fmod(i.positionHCS.x, 4), fmod(i.positionHCS.y, 4));
                half ditherResult = smoothstep(0, _Distance, (distance(posObj, _WorldSpaceCameraPos) - dither));
                clip(ditherResult - 1);


                return float4(0, 0, 0, 0);
            }
            ENDHLSL
        }
    }
}

另一种适配多位置的方案

inline float Dither4x4Bayer(int x, int y)
{
    const float dither[ 16 ] = {
        1, 9, 3, 11,
        13, 5, 15, 7,
        4, 12, 2, 10,
        16, 8, 14, 6
    };
    int r = y * 4 + x;
    return dither[r] / 16; // same # of instructions as pre-dividing due to compiler magic
}

然后,如果需要两个点作为消失点的话

void DitherClip(Varyings input)
{
half dither = Dither4x4Bayer(fmod(input.positionCS.x, 4), fmod(input.positionCS.y, 4));
half clipValue = smoothstep(0, _DitherAmount, (distance(input.positionWSAndFogFactor.xz, _WorldSpaceCameraPos.xz) - dither));
// 1和2是为了适配两个固定的消失点,用min取最小值。此时上面的clipValue是失效的
half clipValue1 = smoothstep(0, _DitherAmount, (distance(_CarDitherPos.xy, input.positionWSAndFogFactor.xz) - dither));
half clipValue2 = smoothstep(0, _DitherAmount, (distance(_CarDitherPos.zw, input.positionWSAndFogFactor.xz) - dither));
clipValue = min(clipValue1,clipValue2);
clip(clipValue - 1);
}

ps里使用也比较简单

文章配图

为什么取min呢

测试过可以发现,clip() 函数剔除的是 > 0 的部分

消除色阶

Dithering:使用8-bit dithering像素点抖动技术,消除色阶。

https://zhuanlan.zhihu.com/p/84434802

https://bartwronski.com/2016/10/30/dithering-in-games-mini-series/

补充方案

射线检测对象是否在后面然后材质改成透明

https://bobsong.cn/1084.html

— END —