Pular para o conteúdo
Fundamento · CoordenadasIniciante

UV e proporção de tela

Transforme a posição de um pixel em coordenadas de 0 a 1, centralize-as e mantenha os círculos redondos.

Como funciona

Um fragment shader roda uma vez por pixel, e a primeira coisa que quase todo shader precisa saber é onde esse pixel está. O GLSL oferece o gl_FragCoord, a posição do pixel na janela, em pixels: gl_FragCoord.xy vale (0.5, 0.5) no centro do pixel do canto inferior esquerdo e cresce em direção ao canto superior direito. Só que pixel é uma unidade incômoda: um shader escrito nela fica diferente em cada tamanho de tela.

Dividir pelo tamanho do canvas resolve isso. O resultado é a coordenada UV: 0 nas bordas esquerda e inferior, 1 nas bordas direita e superior, seja qual for a resolução.

// no escopo do arquivo:
uniform vec2 uResolution; // o tamanho do canvas em pixels, definido pelo código host

// dentro de main():
vec2 uv = gl_FragCoord.xy / uResolution; // 0..1 nos dois eixos

Numa malha, as UVs vêm do modelo: cada vértice guarda uma coordenada 2D que diz onde ele fica numa textura plana, e o rasterizador a interpola ao longo do triângulo. De um jeito ou de outro, o fragment shader recebe um vec2 que vai de 0 a 1 pela superfície. Os cards deste site entregam esse valor pronto em i.uv, com a origem no canto inferior esquerdo.

Veja como cor

O jeito mais rápido de entender uma coordenada é mostrá-la na tela. Escrever uv nos canais vermelho e verde pinta preto no canto inferior esquerdo, vermelho no inferior direito, verde no superior esquerdo e amarelo no superior direito:

#version 300 es
precision highp float;

uniform vec2 uResolution;
out vec4 fragColor;

void main() {
    vec2 uv = gl_FragCoord.xy / uResolution;
    fragColor = vec4(uv, 0.0, 1.0);
}

Essa também é a sua primeira ferramenta de depuração: qualquer valor entre 0 e 1 pode ser conferido escrevendo-o num canal de cor.

Centralizando

Formas radiais ficam mais fáceis de escrever em torno do meio da tela. Dobrar e deslocar leva a origem para o centro e o intervalo para -1..1:

vec2 p = uv * 2.0 - 1.0; // -1..1, com (0, 0) no meio

Agora length(p) é a distância até o centro, o ponto de partida de todo círculo e anel (veja vetores).

Proporção de tela

As UVs esticam junto com o canvas. Num canvas de 1600×900, 0.1 em x cobre 160 pixels, mas 0.1 em y só 90, então um círculo desenhado como length(p) < 0.5 sai como uma elipse. Multiplicar x pela proporção (largura dividida pela altura) faz uma unidade valer o mesmo número de pixels nos dois eixos:

p.x *= uResolution.x / uResolution.y;

Um atalho comum centraliza e corrige numa linha só, dividindo apenas pela altura:

vec2 p = (2.0 * gl_FragCoord.xy - uResolution) / uResolution.y;

Aqui y vai de -1 a 1 e x vai um pouco além numa tela larga. Escolha uma convenção por shader e mantenha: todos os números das formas que vêm depois dependem dela.

Leitura complementar