Unity Shader – CheckerBoard(棋盘格) 等 Pattern 的测试


什么是 CheckerBoard

你可以去翻译一下,就是棋盘格的意思
一般长下图的样子

(是否在很多 DCC 软件可以看到类似的图案,如:Photoshop 的透明底色,或是一些预览图片的软件都使用类似下面的 CheckerBoard 来表示透明部分)
在这里插入图片描述


来个最简单的 CheckerBoard


显示全屏UV

// jave.lin 2021/12/24
// T1 显示 屏幕 UV01 值

Shader "Test/T1_ShowScreenPos01"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            void vert (
                float4 positionOS : POSITION,
                out float4 positionCS : SV_POSITION,
                out float2 screenPos : TEXCOORD0
            )
            {
                positionCS = UnityObjectToClipPos(positionOS);
                float4 sp = ComputeScreenPos(positionCS);
                screenPos = sp.xy / sp.w * _ScreenParams.xy; // jave.lin : screen width, height, 不是 0~1
            }
            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                return fixed4(screenPos / _ScreenParams.xy, 0, 1); // jave.lin : 现在调试效果,先显示 0~1
            }
            ENDCG
        }
    }
}

在这里插入图片描述


显示棋盘格


水平分段一下(但是黑色分割先只有1个像素)

// jave.lin 2021/12/24
// T2 显示棋盘格

Shader "Test/T2_ShowCheckerBox"
{
    Properties
    {
        _CheckerBoardSize("Checker Board Size", Float) = 2
    }
    SubShader
    {
        Pass
        {
        	...
            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                return screenPos.x % _CheckerBoardSize; // 水平分段一下(但是黑色分割先只有1个像素)            }
            ENDCG
        }
    }
}

在这里插入图片描述


填充分阶的内容为 0~1 的渐变值

在这里插入图片描述

// jave.lin 2021/12/24
// T2 显示棋盘格

Shader "Test/T2_ShowCheckerBox"
{
    Properties
    {
        _CheckerBoardSize("Checker Board Size", Float) = 2
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            half _CheckerBoardSize;
            void vert (
                float4 positionOS : POSITION,
                out float4 positionCS : SV_POSITION,
                out float2 screenPos : TEXCOORD0
            )
            {
                positionCS = UnityObjectToClipPos(positionOS);
                float4 sp = ComputeScreenPos(positionCS);
                screenPos = sp.xy / sp.w * _ScreenParams.xy; // jave.lin : screen width, height, 不是 0~1
            }
            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                //return screenPos.x % _CheckerBoardSize; // 水平分段一下(但是黑色分割先只有1个像素)
                return screenPos.x % _CheckerBoardSize / _CheckerBoardSize; // 填充分阶的内容为 0~1 的渐变值
            }
            ENDCG
        }
    }
}


将0~1 渐变值一分为二

将 0 ~ 1 的渐变值一分为二的思路可以使用:round(val) 函数,等价于 val > 0.5 ? 1 : 0,二值化

在这里插入图片描述

screenPos.x 调整为 y,可以输出纵向的色阶

return round(screenPos.y % _CheckerBoardSize / _CheckerBoardSize);

在这里插入图片描述


最终棋盘格:输出纵横向的相异为真的像素值

// jave.lin 2021/12/24
// T2 显示棋盘格

Shader "Test/T2_ShowCheckerBox"
{
    Properties
    {
        _CheckerBoardSize("Checker Board Size", Float) = 2
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            half _CheckerBoardSize;
            void vert (
                float4 positionOS : POSITION,
                out float4 positionCS : SV_POSITION,
                out float2 screenPos : TEXCOORD0
            )
            {
                positionCS = UnityObjectToClipPos(positionOS);
                float4 sp = ComputeScreenPos(positionCS);
                screenPos = sp.xy / sp.w * _ScreenParams.xy; // jave.lin : screen width, height, 不是 0~1
            }
            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                //return screenPos.x % _CheckerBoardSize; // 水平分段一下(但是黑色分割先只有1个像素)
                //return screenPos.x % _CheckerBoardSize / _CheckerBoardSize; // 填充分阶的内容为 0~1 的渐变值
                //return round(screenPos.x % _CheckerBoardSize / _CheckerBoardSize); // 将0~1 渐变值一分为二
                //return round(screenPos.y % _CheckerBoardSize / _CheckerBoardSize); // 纵向的:将0~1 渐变值一分为二
                screenPos = round(screenPos % _CheckerBoardSize / _CheckerBoardSize); // 纵横向一起计算
                return screenPos.x != screenPos.y; // 相异为真输出1,但是,cg 函数没有运算吗,有点无语,直接用 |, &, !, ^ 都会编译不过
            }
            ENDCG
        }
    }
}

在这里插入图片描述

从上图分析我们可以知道,可以将 相异为真 的结果输出即可

在这里插入图片描述


其他 CheckerBoard 的样式

有了前面的 简单的 CheckerBoard 方式

下面我们搞一些其他的 pattern,都是经验公式(实验出来的)


同样的,使用 sin 都所有色阶抖动的函数(但是 0 ~ 1 色阶过度太平滑)

// jave.lin 2021/12/24
// T3 显示其他棋盘格

Shader "Test/T3_ShowOhterCheckerBox"
{
    Properties
    {
        _CheckerBoardSize("Checker Board Size", Float) = 2
        _CheckerBoardWrap("Checker Board Wrap", Float) = 2
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            half _CheckerBoardSize;
            half _CheckerBoardWrap;
            void vert (
                float4 positionOS : POSITION,
                out float4 positionCS : SV_POSITION,
                out float2 screenPos : TEXCOORD0
            )
            {
                positionCS = UnityObjectToClipPos(positionOS);
                float4 sp = ComputeScreenPos(positionCS);
                screenPos = sp.xy / sp.w * _ScreenParams.xy; // jave.lin : screen width, height, 不是 0~1
            }
            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                return sin(screenPos.x * _CheckerBoardSize) * _CheckerBoardWrap; // 同样的,使用 sin 都所有色阶抖动的函数(但是 0 ~ 1 色阶过度太平滑)
            }
            ENDCG
        }
    }
}

在这里插入图片描述


和之前一样,可以 round 一下二值化来分色阶

            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                //return sin(screenPos.x * _CheckerBoardSize) * _CheckerBoardWrap; // 同样的,使用 sin 都所有色阶抖动的函数(但是 0 ~ 1 色阶过度太平滑)
                return round(sin(screenPos.x * _CheckerBoardSize) * _CheckerBoardWrap); // 和之前一样,可以 round 一下二值化来分色阶
            }

在这里插入图片描述


同样的相异为真输出 checkerboard

            fixed4 frag(float4 positionCS : SV_POSITION, float2 screenPos : TEXCOORD0) : SV_Target
            {
                //return sin(screenPos.x * _CheckerBoardSize) * _CheckerBoardWrap; // 同样的,使用 sin 都所有色阶抖动的函数(但是 0 ~ 1 色阶过度太平滑)
                //return round(sin(screenPos.x * _CheckerBoardSize) * _CheckerBoardWrap); // 和之前一样,可以 round 一下二值化来分色阶
                screenPos = round(sin(screenPos * _CheckerBoardSize) * _CheckerBoardWrap); // 同样的相异为真输出 checkerboard
                return screenPos.x != screenPos.y;
            }

在这里插入图片描述

但是可以看到,这个棋盘格的效果和之前的不太一样

然后上面的 return screenPos.x != screenPos.y; 可以换成:return screenPos.x == screenPos.y;,我向你应该知道什么结果(反向结果)


其他花样


T1

                // s: _CheckerBoardSize: 0.04, _CheckerBoardWrap: 0~+infi, 0.86
                return any(round(sin(screenPos * _CheckerBoardSize) * _CheckerBoardWrap));

在这里插入图片描述


T2

                // s: _CheckerBoardSize: 0~+infi, _CheckerBoardWrap: 0~1
                return all(round(sin(screenPos * _CheckerBoardSize) * _CheckerBoardWrap));

在这里插入图片描述


T3

                // s: _CheckerBoardSize: 0.1, _CheckerBoardWrap: 0.86
                screenPos = round(tan(screenPos * _CheckerBoardSize) * _CheckerBoardWrap);
                return screenPos.x * screenPos.y;

在这里插入图片描述


T4

                // s: _CheckerBoardSize: 0.04, _CheckerBoardWrap: 0~+infi, 0.86
                screenPos = round(tan(screenPos * _CheckerBoardSize) * _CheckerBoardWrap);
                return screenPos.x == screenPos.y;

在这里插入图片描述


etc.

	// s: _CheckerBoardSize: 25, _CheckerBoardWrap: 25
	sp = round(floor(sp % _CheckerBoardWrap) / _CheckerBoardSize);
	//sp = round(floor(sp / _CheckerBoardWrap) / _CheckerBoardSize);
	//sp = round(floor(sp * (1.0 / _CheckerBoardWrap)) * (1.0 / _CheckerBoardSize));
	//sp = round(floor(sp * (1.0 / _CheckerBoardWrap)) * (1.0 / _CheckerBoardSize));
	//sp = pow(sp, _CheckerBoardSize) * pow(sp, _CheckerBoardWrap);
	//return all(sp);
	//return any(sp);
	//return sp.x != sp.y;
	//return sp.x == sp.y;
	//return round(sin(sp.x) * sin(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return (cos(sp.x) * cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(cos(sp.x) * cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(cos(sp.x) / cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(cos(sp.x) % cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(cos(sp.x) - cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(sin(sp.x) - cos(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(pow(sin(sp.x), cos(sp.y))); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	return round(tan(sp.x) * tan(sp.y)); // _CheckerBoardSize:0~2*_CheckerBoardWrap
	//return round(tan(sp.x) % tan(sp.y)); // _CheckerBoardSize:2, *_CheckerBoardWrap: 100

上面的都是随便试出来的效果


这些东西能用在什么地方

如果我们将, checker board ==0 的黑色像素都 discard 掉

将 checker board 的 grid 弄得足够小,是不是就有点向一些游戏效果中的简单的 dither 作为透明,或是马赛克显示效果(如:原神 中,你如果把镜头往人物胯部看的话,就发现人物被 checker board pattern 的方式给 discard 掉部分像素,让想看的地方看得不太清晰,而不至于被和的得情况)

或是有些游戏会在:XRay 时显示 checker board 的像素,一般会显示 rim 效果的比较多


后续我会再写个,这些 checker board,不时通过程序化生成,而是 Texture 简单应用的场景,其实就时类似:马赛克分色阶 来溶解的效果


Project

TestingCheckerBoardPattern_unity_2019.4.30f1

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>