栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

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

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
  • 什么是 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
        }
    }
}

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


优化

前面演示的的部分运算可以优化的:

screenPos = round(screenPos % _CheckerBoardSize / _CheckerBoardSize); // 纵横向一起计算

调整为:直接除法后,取小数部分,然后 二值化即可

screenPos = round(frac(screenPos / _CheckerBoardSize)); // 纵横向一起计算

f ( x , y ) = ( m o d ( x y ) ) f(x,y)= pmod{(frac{x}{y})} f(x,y)=(mod(yx​))


其他 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

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


最后 二值化,我们可以使用 step + 阈值 来替代 round 函数,效果可以自行测试

// jave.lin 2021/12/24
// T4 显示其他棋盘格(带 illum 阈值)

Shader "Test/T4_ShowOhterCheckerBoxWithIllumThreshold"
{
    Properties
    {
        _CheckerBoardSize ("Checker Board Size", Float) = 0.1
        _CheckerBoardWrap ("Checker Board Wrap", Float) = 1
        _CheckerBoardIlluminationThreshold ("Checker Board Illumination Threshold", Range(-1, 1)) = 0.0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            half _CheckerBoardSize;
            half _CheckerBoardWrap;
            half _CheckerBoardIlluminationThreshold;
            //#define IllumThresholdClamp(v) round(v)
            #define IllumThresholdClamp(v) step(_CheckerBoardIlluminationThreshold, v)
            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 {
                screenPos = IllumThresholdClamp(sin(screenPos * _CheckerBoardSize) * _CheckerBoardWrap);
                return screenPos.x == screenPos.y;
            }
            ENDCG
        }
    }
}


这些东西能用在什么地方

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

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

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


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

下面是偶然发现 unity 2019.4.30f1 中的某个 LOD 淡入淡出的 Dither Shader 处理:
再 UnityCG.cginc 文件下

#ifdef LOD_FADE_CROSSFADE
    #define UNITY_APPLY_DITHER_CROSSFADE(vpos)  UnityApplyDitherCrossFade(vpos)
    sampler2D unity_DitherMask;
    void UnityApplyDitherCrossFade(float2 vpos)
    {
        vpos /= 4; // the dither mask texture is 4x4
        float mask = tex2D(unity_DitherMask, vpos).a;
        float sgn = unity_LODFade.x > 0 ? 1.0f : -1.0f;
        clip(unity_LODFade.x - mask * sgn);
    }
#else
    #define UNITY_APPLY_DITHER_CROSSFADE(vpos)
#endif

还是可以使用在 semitransparent,可以参考以下:cat like coding 博客中的:Semitransparent Shadows


Project

TestingCheckerBoardPattern_unity_2019.4.30f1

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/900785.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号