文章目錄
- 什么是 CheckerBoard
- 來個最簡單的 CheckerBoard
- 顯示全屏UV
- 顯示棋盤格
- 水平分段一下(但是黑色分割先只有1個像素)
- 填充分階的內容為 0~1 的漸變值
- 將0\~1 漸變值一分為二
- 最終棋盤格:輸出縱橫向的相異為真的像素值
- 其他 CheckerBoard 的樣式
- 同樣的,使用 sin 都所有色階抖動的函式(但是 0 ~ 1 色階過度太平滑)
- 和之前一樣,可以 round 一下二值化來分色階
- 同樣的相異為真輸出 checkerboard
- 其他花樣
- T1
- T2
- T3
- T4
- etc.
- 這些東西能用在什么地方
- Project
什么是 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/392259.html
標籤:其他
上一篇:AB打包策略
