Tabuleiro de xadrez
Duas cores alternando numa grade, só com floor e mod.
- 01ObjetivoPadrão
- 02TécnicaGrades e ladrilhos
- 03ExecuçãoTabuleiro de xadrez
Parâmetros
Copie para a sua plataforma
- GLSLCopiar
- GodotCopiar
Cadeia de nós
Montada passo a passo, no estilo do GLSL, com os nomes de nós dele.
Montada passo a passo, no estilo do Godot, com os nomes de nós dele.
Este card ainda não tem versão para Unreal Engine 5, então os nós mostram os nomes do GLSL.
Este card ainda não tem versão para Unity, então os nós mostram os nomes do GLSL.
Este card ainda não tem versão para Blender, então os nós mostram os nomes do GLSL.
floorFloorfloorfloorfloor UV01 Numerar as células
modRemaindermodmodmod Entrada02 Par ou ímpar
mixMixmixmixmix Entrada03 Escolher a cor
Como funciona
Um tabuleiro de xadrez é a menor grade útil: numere as células e pinte cada uma conforme o número dela seja par ou ímpar.
Passo a passo
- Numerar as células.
i.uvvai de 0 a 1 de um lado a outro do quad. Multiplicá-la pelo número de colunas e de linhas estica esse intervalo para 0–16 na horizontal e 0–10 na vertical, e ofloordescarta a parte fracionária, então todo pixel de uma célula recebe o mesmo par de inteiros, de(0, 0)no canto inferior esquerdo até(15, 9). Duas contagens em vez de uma deixam as células quadradas num quad que não é quadrado: a prévia é 16:10. Dividir pelas contagens traz o par de volta para 0–1, o canto de cada célula, que a prévia mostra como uma escada vermelha e verde. É a divisão da UVUma posição 2D numa superfície ou na tela, em geral de 0 a 1 nos dois eixos. Shaders desenham formas e padrões como funções dela.FundamentoCoordenadas UV
Uma posição 2D numa superfície ou na tela, em geral de 0 a 1 nos dois eixos. Shaders desenham formas e padrões como funções dela.
uv = gl_FragCoord.xy / resolutionFontesdo fundamento de fract e floor, usando só a metade da célula.- gl_FragCoord (abre em uma nova aba)Referência
- UV mapping (abre em uma nova aba)Referência
- Par ou ímpar.
dot(cell, counts)desfaz a divisão e soma as duas partes: coluna mais linha. Andar uma célula para a direita ou para cima soma 1 a esse total, então vizinhas sempre diferem em um, emod(…, 2.0)o transforma em 0 nas células pares e 1 nas ímpares. Vizinhas na diagonal somam 2 e mantêm a mesma cor, que é exatamente o xadrez. A divisão e a multiplicação deixam a soma um fio fora de um número inteiro, então orounda acerta antes de omodler. - Escolher a cor.
mix(p_even, p_odd, parity)comparityvalendo exatamente 0 ou 1 é uma escolha entre duas opções semif: devolve uma cor ou a outra, nunca uma mistura.
O floor importa no passo 1. int() trunca em direção ao zero, então -0.5 e 0.5 cairiam os dois na célula 0, e uma
UV deslocada ou repetida mostraria uma célula com o dobro da largura onde cruza o zero. floor arredonda em direção ao
menos infinito e mantém todas as células do mesmo tamanho.
Notas de produção
Custo. Sete operações por pixel: uma multiplicação, um floor, uma divisão, um dot, um round, um mod e um mix. Uma versão que pula a etapa visível em 0–1 precisa de só cinco (multiplicação, floor, soma, mod, mix). Nenhuma leitura de textura, uma passada. É praticamente o padrão mais barato que existe e não ocupa memória, então pode cobrir uma fase inteira.
Aliasing à distância. As bordas são duras: cada pixel amostra o padrão uma vez, no seu centro, e recebe uma cor ou
a outra. De perto isso fica perfeitamente nítido. Suba as colunas e linhas para mais de mil na prévia, ou ponha o
material num piso que vai até o horizonte, e as células ficam menores que um pixel. As amostras passam a cair nas
células quase ao acaso, e o tabuleiro se desfaz em faixas falsas e num tabuleiro falso mais grosso (moiré); numa
superfície curva ou que se afasta elas viram curvas, e com a câmera em movimento, cintilação: um caso clássico de
aliasingO padrão falso que surge quando um sinal muda mais rápido do que é amostrado. Num shader, detalhes menores que um pixel viram bordas serrilhadas, cintilação e moiré, porque cada pixel tira uma única amostra do seu centro. Aliasing O padrão falso que surge quando um sinal muda mais rápido do que é amostrado. Num shader, detalhes menores que um pixel viram bordas serrilhadas, cintilação e moiré, porque cada pixel tira uma única amostra do seu centro.
A solução é tirar a média do padrão sobre a área que cada pixel cobre. fwidth(i.uv * counts) diz quantas células um
pixel abrange, e o artigo sobre procedurais filtráveis nas fontes deduz a partir disso o xadrez exato filtrado por
caixa, que desbota o tabuleiro suavemente para o cinza médio conforme as células encolhem. As derivadas em espaço de
tela vêm mais adiante na ordem de estudo; voltar a este card para filtrá-lo é o exercício de lá.
Quando escrever o seu. Num editor de nós, o quadriculado nativo faz a mesma conta. Escreva à mão quando a grade precisa fazer algo que o nó não faz: numerar as células para tons aleatórios, deslocar uma fileira sim, outra não para fazer tijolos, ou filtrar as bordas.
Monte no GLSL
O botão de copiar entrega um shader de fragmento WebGL2 completo: #version 300 es, os uniforms, a struct Inputs e um
main() que a preenche e chama effect(). Para usá-lo no seu próprio código WebGL2, desenhe um quad (ou um triângulo
de tela cheia) cujo shader de vértice escreve v_uv de 0 a 1, além de v_position, v_normal e v_view (este card só
lê a UV, então constantes servem), compile o par e defina os uniforms:
p_columnsep_rowscomo floats. Mantenha a proporção entre eles igual à do canvas (16 e 10 para um canvas de 800 × 500) e as células ficam quadradas.p_evenep_oddcomo cores lineares. Uma cor tirada de um seletor está em sRGB; decodifique na CPU com a curva sRGB (ouMath.pow(c, 2.2)por canal, que chega perto) antes de enviar.
O shader escreve a cor final já codificada em sRGB, então ela vai direto para o canvas. Num pipeline que codifica
depois (renderizando antes num alvo linear), tire a codificação do fim do main().
Numa malha 3D, o mesmo effect() funciona sobre a UV0 da malha. Use-o para conferir UVs: células quadradas significam
que a UV não está esticada, e células que crescem, encolhem ou entortam mostram onde ela está.
Monte no Godot
O VisualShader não tem nó de xadrez. O grafo de nós abaixo segue os passos acima, mas soma as coordenadas inteiras da célula com um VectorDecompose e um Add em vez de dividir e fazer um produto escalar; a paridade sai a mesma com menos nós.
O botão de copiar entrega um shader canvas_item. Numa cena 2D, adicione um ColorRect (ou qualquer Control ou
Sprite2D), dê a ele um novo ShaderMaterial e cole o código num novo Shader desse material. O tabuleiro preenche o
retângulo do nó: columns e rows ficam no inspetor, em Shader Parameters, e a proporção entre eles deve bater com o
tamanho do nó (16 e 10 para um retângulo de 320 × 200) para as células saírem quadradas. even e odd são seletores
de cor.
Numa malha 3D. O mesmo effect() funciona num shader spatial, sobre a UV da malha. Comece um novo Shader com
shader_type spatial; e render_mode unshaded;, cole os uniforms e o effect() e escreva um fragment() que preenche
Inputs a partir de UV e faz ALBEDO = effect(i).rgb;. Shaders spatial decodificam os uniforms source_color para
linear e codificam a saída sozinhos, então tire as chamadas a u_toLinear lá. Tire o unshaded num piso que as luzes
da cena devem iluminar.
Como grafo de nós. Num VisualShader no modo CanvasItem: a entrada UV num Multiply (Vector2) com um Vector2Parameter para a contagem de células, depois Floor, depois VectorDecompose, depois Add (x + y), depois Remainder por 2.0, e esse resultado como peso de um Mix entre dois ColorParameter, ligado à saída Color.
Ainda não há tutorial de Unreal Engine 5 para este card.
Ainda não há tutorial de Unity para este card.
Ainda não há tutorial de Blender para este card.
Código
#version 300 es// Grids & tiling → Checkerboard, web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform float p_columns; // Columns: 16.0 (1.0 to 2000.0)uniform float p_rows; // Rows: 10.0 (1.0 to 1250.0)uniform vec3 p_even; // Even cells: vec3(0.93, 0.91, 0.86), sRGB; upload it decoded to linearuniform vec3 p_odd; // Odd cells: vec3(0.16, 0.17, 0.2), sRGB; upload it decoded to linearin vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};vec3 u_toSrgb(vec3 c) { return mix(c * 12.92, 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));}vec4 effect(Inputs i) { vec2 counts = vec2(p_columns, p_rows); // floor, not int(): int() truncates toward zero, so when the UV is offset or tiled past 0..1 the two cells on // either side of 0 would become one double-width cell. Dividing by the counts keeps each cell's corner in 0..1 vec2 cell = floor(i.uv * counts) / counts; // dot(cell, counts) is column + row as whole numbers; round absorbs the float error the divide left float parity = mod(round(dot(cell, counts)), 2.0); vec3 color = mix(p_even, p_odd, parity); return vec4(color, 1.0);}void main() { Inputs i; i.uv = v_uv; i.position = v_position; // Interpolation shortens the vectors between vertices i.normal = normalize(v_normal); i.view = normalize(v_view); i.time = u_time; i.fragCoord = gl_FragCoord.xy; i.resolution = u_resolution; vec4 frag_out = effect(i); // The output is sRGB: clamped and encoded once, here, with no tone mapping; opaque, so alpha is 1 fragColor = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), 1.0);}// Grids & tiling → Checkerboard, Godot 4. From Creative Center, MIT license.shader_type canvas_item;// Unshaded shows the effect's color as is, like the site's preview; scene lights don't touch itrender_mode unshaded;uniform float columns : hint_range(1.0, 2000.0, 1.0) = 16.0;uniform float rows : hint_range(1.0, 1250.0, 1.0) = 10.0;uniform vec3 even : source_color = vec3(0.93, 0.91, 0.86);uniform vec3 odd : source_color = vec3(0.16, 0.17, 0.2);struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};// Godot's 2D renderer works in sRGB (HDR 2D off, the default), so the frame decodes color params and sRGB// textures to linear itself and encodes the result. With HDR 2D on, make these return c unchanged.vec3 u_toLinear(vec3 c) { return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));}vec3 u_toSrgb(vec3 c) { return mix(c * 12.92, 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));}vec4 effect(Inputs i) { vec2 counts = vec2(columns, rows); // floor, not int(): int() truncates toward zero, so when the UV is offset or tiled past 0..1 the two cells on // either side of 0 would become one double-width cell. Dividing by the counts keeps each cell's corner in 0..1 vec2 cell = floor(i.uv * counts) / counts; // dot(cell, counts) is column + row as whole numbers; round absorbs the float error the divide left float parity = mod(round(dot(cell, counts)), 2.0); vec3 color = mix(u_toLinear(even), u_toLinear(odd), parity); return vec4(color, 1.0);}void fragment() { Inputs i; // UV starts at the top-left; the effect expects GLSL's bottom-left i.uv = vec2(UV.x, 1.0 - UV.y); // A flat quad facing the viewer, the same as the site's 2D preview i.position = vec3(i.uv * 2.0 - 1.0, 0.0); i.normal = vec3(0.0, 0.0, 1.0); i.view = vec3(0.0, 0.0, 1.0); i.time = TIME; // FRAGCOORD's origin is the top-left too i.resolution = 1.0 / SCREEN_PIXEL_SIZE; i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y); vec4 frag_out = effect(i); COLOR = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), 1.0);}Ainda não há código de Unreal Engine 5 para este card.
Ainda não há código de Unity para este card.
Ainda não há código de Blender para este card.
Leitura complementar
- Patterns (abre em uma nova aba)Livro
- floor (abre em uma nova aba)Referência
- mod (abre em uma nova aba)Referência
- Filterable procedurals (abre em uma nova aba)Artigo
- Aliasing (abre em uma nova aba)Referência
- CanvasItem shaders (abre em uma nova aba)Documentação