Mostrar valores como cor
Escreva qualquer valor na saída para enxergá-lo e aprenda a ler o que as cores dizem.
Como funciona
Um shader de fragmentoO programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele. Shader de fragmento O programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele.print. Ele roda milhões de vezes por quadro, na GPU,
e a única coisa que volta é uma cor por pixel. Então a cor é o depurador: quando um valor parece errado, escreva-o na
saída e olhe para ele. Toda prévia de passo nos cards deste site é exatamente isso, uma variável mostrada como cor.
Um valor, em cinza
Um float entre 0 e 1 se lê melhor em cinza: preto é 0, branco é 1. Coloque o mesmo valor nos três canais:
float d = length(p); // o valor que você quer inspecionar
fragColor = vec4(vec3(d), 1.0); // preto no centro, mais claro para fora
Um Coordenadas 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.vec2 cabe no vermelho e no verde, como acontece com a 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.uv = gl_FragCoord.xy / resolutionvec3 preenche os três canais.
Para ler um só componente de um vetor, isole-o: fragColor = vec4(vec3(n.y), 1.0); mostra apenas o y em cinza.
Valores fora de 0 a 1
A tela limita a saída, então tudo abaixo de 0 aparece preto e tudo acima de 1 aparece branco. Uma região inteira de preto chapado pode esconder um valor de -0.01 ou de -500. Remapeie antes:
- Um intervalo de -1 a 1, como uma direção ou uma normalUm vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.Fundamento
Normal
Um vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.
Fontes:v * 0.5 + 0.5leva o valor para 0..1. O que aponta para +x fica vermelho-rosado, +y verde-claro, +z azul-claro; um vetor nulo vira 0.5 em todos os canais. - Um intervalo grande, como uma distância em unidades do mundo ou um tempo em segundos: divida pelo maior valor
que você espera, ou mostre
fract(v), que se repete a cada 1 unidade e desenha curvas de nível pelo campo. - Um sinal que importa, como o de uma distância com sinal: dê uma cor própria aos negativos e outra aos positivos. Associar valores a cores arbitrárias para tornar uma propriedade visível se chama falsa cor.
// vermelho onde v é negativo, verde onde é positivo, preto no zero
vec3 signedView(float v) {
return vec3(max(-v, 0.0), max(v, 0.0), 0.0);
}
// faixas finas a cada 0.1 unidade: valores iguais caem na mesma faixa
float contours(float v) {
return step(0.9, fract(v * 10.0));
}
Lendo números exatos
O olho é ruim para julgar brilho, e neste site o código em volta do efeito codifica a saída em sRGB, então um valor de
0.5 aparece bem mais claro que um cinza médio. Para conferir um número, compare em vez de estimar no olho:
step(0.5, v) fica branco exatamente onde v chega a 0.5, e uma tela dividida mostra duas alternativas lado a lado.
vec3 a = vec3(smoothstep(0.2, 0.8, x));
vec3 b = vec3(x * x * (3.0 - 2.0 * x));
vec3 color = uv.x < 0.5 ? a : b; // metade esquerda: a, metade direita: b
NaN e infinito
Uma divisão por zero, a raiz quadrada de um número negativo ou o normalize de um vetor nulo podem gerar NaN (not a
number, “não é um número”) ou infinito. O NaN se espalha por toda operação que encosta nele, e cada GPU o mostra de um
jeito: preto, branco ou aleatório. O GLSL ES 3.00 tem isnan e isinf, então dá para pintar os pixels ruins de uma
cor que nada mais usa:
if (isnan(v) || isinf(v)) {
fragColor = vec4(1.0, 0.0, 1.0, 1.0); // magenta: ache onde isso começa
return;
}
Alguns compiladores otimizam supondo que NaN nunca acontece, então um resultado limpo não é prova. Depois de marcar um pixel, volte pela cadeia mostrando cada valor anterior até o magenta sumir.
Percorra a cadeia
A maioria dos shaders é uma cadeia: coordenadas, depois uma distância, depois uma máscara, depois uma cor. Quando a imagem final está errada, mostre o primeiro elo, depois o seguinte, e pare no primeiro que parecer errado. Ele, ou a entrada dele, guarda o bug. As prévias de passo nos cards seguem essa mesma ordem pelo mesmo motivo.
Numa engine, garanta que o valor chegue à tela sem alteração. No Godot, escrever em ALBEDO faz o valor ser iluminado
e sombreado; enquanto depura, adicione render_mode unshaded;, e aí ALBEDO = valor; mostra o valor cru. Escrever em
EMISSION com ALBEDO = vec3(0.0); não basta: o SPECULAR padrão de 0.5 ainda soma luzes e reflexos do céu por cima,
então defina também SPECULAR = 0.0; se for por esse caminho.
Leitura complementar
- Hello world! (abre em uma nova aba)Livro
- False color (abre em uma nova aba)Referência
- NaN (abre em uma nova aba)Referência