Directorio de artículos
- ¿Qué es el tablero de ajedrez?
- Ven al CheckerBoard más simple
-
- Mostrar UV a pantalla completa
- Mostrar tablero de ajedrez
-
- Segmentarlo horizontalmente (pero el segmento negro es solo 1 píxel primero)
- El contenido del paso de relleno es un valor de gradiente de 0 a 1
- Divide el valor de gradiente 0 ~ 1 en dos
- Tablero de ajedrez final: las diferencias verticales y horizontales de salida son valores de píxeles reales
- mejoramiento
- Otros estilos de tablero de ajedrez
- ¿Dónde se pueden usar estas cosas?
- Proyecto
¿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)
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
}
}
}
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
}
}
}
El contenido del paso de relleno es un valor de gradiente de 0 a 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
}
}
}
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
screenPos.x
Ajuste a y
, puede generar una gradación vertical
return round(screenPos.y % _CheckerBoardSize / _CheckerBoardSize);
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
}
}
}
Del análisis de la figura anterior, podemos saber que podemos generar el resultado de que la diferencia es verdadera.
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) )
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
}
}
}
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 一下二值化来分色阶
}
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;
}
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));
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
Los anteriores son todos efectos aleatorios.
Finalmente, para la binarización, podemos usar step
+ umbral para reemplazar la round
funció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.cginc
archivo
#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