Unity Shader: prueba de patrón como CheckerBoard (tablero de ajedrez)


¿Qué es el tablero de ajedrez?

Puedes traducirlo, ese es el significado del tablero de ajedrez, que
generalmente se parece a la imagen de abajo

(Si se pueden ver patrones similares en muchos software DCC, como: el fondo transparente de Photoshop, o algún software que previsualice imágenes use CheckerBoard como el siguiente para representar partes transparentes)
inserte la descripción de la imagen aquí


Ven al CheckerBoard más simple


Mostrar UV a pantalla completa

// 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
        }
    }
}

inserte la descripción de la imagen aquí


Mostrar tablero de ajedrez


Segmentarlo horizontalmente (pero el segmento negro es solo 1 píxel primero)

// 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
        }
    }
}

inserte la descripción de la imagen aquí


El contenido del paso de relleno es un valor de gradiente de 0 a 1

inserte la descripción de la imagen aquí

// 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
        }
    }
}


Divide el valor de gradiente 0 ~ 1 en dos

Se puede usar la idea de dividir el valor del gradiente de 0 ~ 1 en dos: round(val)función, equivalente val > 0.5 ? 1 : 0, binarización

inserte la descripción de la imagen aquí

screenPos.xAjuste a y, puede generar una gradación vertical

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

inserte la descripción de la imagen aquí


Tablero de ajedrez final: las diferencias verticales y horizontales de salida son valores de píxeles reales

// 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
        }
    }
}

inserte la descripción de la imagen aquí

Del análisis de la figura anterior, podemos saber que podemos generar el resultado de que la diferencia es verdadera.

inserte la descripción de la imagen aquí


mejoramiento

Algunas de las operaciones demostradas anteriormente se pueden optimizar:

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

Ajustado a: después de la división directa, tomar la parte fraccionaria y luego binarizar

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

f ( x , y ) = ( mod ( xy ) ) f(x,y)= \pmod{(\frac{x}{y})}f ( x ,y)=( modo _ _(yx) )

inserte la descripción de la imagen aquí


Otros estilos de tablero de ajedrez

Con el anterior método CheckerBoard simple

A continuación, haremos algunos otros patrones, todos los cuales son fórmulas empíricas (experimentales)


De manera similar, usar sin es una función de todos los niveles de tramado (pero los niveles 0 ~ 1 son demasiado suaves)

// 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
        }
    }
}

inserte la descripción de la imagen aquí


Como antes, puede redondear la binarización para separar los niveles de color

            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 一下二值化来分色阶
            }

inserte la descripción de la imagen aquí


La misma diferencia es cierta para generar el tablero de ajedrez.

            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;
            }

inserte la descripción de la imagen aquí

Pero se puede ver que el efecto de este damero no es el mismo que el anterior

Entonces lo anterior return screenPos.x != screenPos.y;se puede reemplazar por: return screenPos.x == screenPos.y;, te digo que resultado debes saber (resultado inverso)


otros patrones


T1

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

inserte la descripción de la imagen aquí


T2

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

inserte la descripción de la imagen aquí


T3

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

inserte la descripción de la imagen aquí


T4

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

inserte la descripción de la imagen aquí


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

Los anteriores son todos efectos aleatorios.


Finalmente, para la binarización, podemos usar step+ umbral para reemplazar la roundfunción , y el efecto se puede probar por sí mismo

// 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
        }
    }
}


¿Dónde se pueden usar estas cosas?

Si descartamos todos los píxeles negros con tablero de ajedrez ==0

Si la cuadrícula del tablero de ajedrez se hace lo suficientemente pequeña, ¿es un poco transparente para el tramado simple en algunos efectos del juego, o un efecto de visualización de mosaico (por ejemplo, en Genshin Impact, si miras la entrepierna del personaje, solo Se encuentra que el carácter se descarta por el patrón del tablero de ajedrez, por lo que el lugar que desea ver no es muy claro y no se mezclará)

O algunos juegos mostrarán los píxeles del tablero de ajedrez en: rayos X y, en general, mostrarán más efectos de borde.


Escribiré más adelante, estos tableros de ajedrez se generan mediante programación de vez en cuando, pero son escenarios de aplicación simples de Texture, que en realidad son similares de vez en cuando: el efecto de disolver niveles de mosaico

El siguiente es el procesamiento de Dither Shader que accidentalmente encontró un LOD en Unity 2019.4.30f1 para aparecer y desaparecer:
luego, debajo del UnityCG.cgincarchivo

#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

Todavía se puede usar en semitransparente, puede consultar lo siguiente: cat like coding blog: Sombras semitransparentes


Proyecto

TestingCheckerBoardPattern_unity_2019.4.30f1

Supongo que te gusta

Origin blog.csdn.net/linjf520/article/details/122131996
Recomendado
Clasificación