Transformações 2D
Translade, gire e escale o espaço UV com vetores e uma mat2, e entenda por que você move o espaço em vez da forma.
Como funciona
Uma forma num shader de fragmento é uma função da coordenada do pixel: um círculo é “os pixels onde length(p) fica
abaixo de 0.3”. Não existe um objeto círculo para mover. Para mover, girar ou redimensionar a forma, você muda a
coordenada antes que a função da forma a veja. Todo padrão deste site, de um tabuleiro de xadrez a um campo de ruído,
se move do mesmo jeito.
Translação
Subtrair um deslocamento da coordenada move a forma por esse deslocamento:
vec2 p = uv * 2.0 - 1.0; // centralizada, -1..1
vec2 q = p - vec2(0.4, 0.2); // o centro do círculo agora fica em (0.4, 0.2)
float circle = step(length(q), 0.3);
No começo parece invertido. A forma é desenhada onde q fica perto de zero, e q é zero no pixel p = (0.4, 0.2).
Subtrair move a forma para a frente. Essa inversão é o coração deste fundamento: você está transformando o espaço em
que a forma é desenhada, então toda operação roda como o inverso da que você quer que a forma faça.
Escala
Dividir a coordenada aumenta a forma: p / 2.0 faz a coordenada crescer na metade da velocidade, então tudo aparece
com o dobro do tamanho. Multiplicar diminui a forma e repete mais vezes um padrão: uv * 8.0 encaixa oito casas do
tabuleiro de um lado a outro. Dividir x e y por valores diferentes estica a forma, como faz a proporção de tela sem
correção. Uma distância medida no espaço escalado também fica escalada; isso importa para os campos de distância, mais
adiante na ordem.
Rotação
A rotação por um ângulo Matriz Uma grade de números (mat2, mat3, mat4) que leva um vetor a outro numa única multiplicação. Rotação, escala e, com coordenadas homogêneas, translação e projeção são todas matrizes.a em torno da origem leva (x, y) a (x cos a − y sen a, x sen a + y cos a). Isso é uma
multiplicação por matrizUma grade de números (mat2, mat3, mat4) que leva um vetor a outro numa única multiplicação. Rotação, escala e, com coordenadas homogêneas, translação e projeção são todas matrizes.rotate(a) = [cos a −sin a; sin a cos a]
mat2 rotate2d(float a) {
float c = cos(a);
float s = sin(a);
return mat2(c, s, -s, c); // colunas: (c, s) e (-s, c)
}
vec2 q = rotate2d(-angle) * p; // gira a forma em +angle, no sentido anti-horário
O sinal de menos é o mesmo inverso de antes: para girar a forma no sentido anti-horário, gire o espaço no sentido
horário. angle fica em radianos: uma volta completa é 2π, uns 6.283, e 90° é π / 2.
As matrizes do GLSL são column-major, organizadas por coluna: o construtor preenche a primeira coluna, depois a
segunda. mat2(c, s, -s, c) tem (c, s) como primeira coluna, então é a matriz que os livros de matemática escrevem
como [c −s; s c]. Ler os quatro números como linhas dá a matriz transposta, que numa rotação é a inversa: ela gira
para o outro lado. Esse é o bug de rotação mais comum. O mesmo fato dá um atalho: p * m multiplica pela transposta,
então p * rotate2d(angle) é o mesmo que rotate2d(-angle) * p.
Ordem e pivôs
Translação, rotação e escala não comutam: girar e depois mover não é o mesmo que mover e depois girar. Para girar uma forma em torno do próprio centro em vez da origem, mova o espaço para que o centro fique na origem, gire e desenhe ali:
vec2 center = vec2(0.4, 0.2);
// a forma, em center, girada em angle em torno do próprio centro
vec2 q = rotate2d(-angle) * (p - center);
Para uma transformação completa de uma forma (escalar por s, depois girar por a, depois mover até t), o espaço
desfaz esses passos na ordem contrária: subtrai t, gira por -a, divide por s.
vec2 q = (rotate2d(-a) * (p - t)) / s;
Corrija a proporção de tela antes de qualquer rotação. Girar em UVs esticadas mistura x e y enquanto eles têm unidades diferentes, e a forma se inclina enquanto gira.
Transformando padrões e texturas
As mesmas operações valem para qualquer coisa que leia uma coordenada. Gire a uv antes do fract e a grade inteira
gira. Escale ao longo do tempo e o padrão faz zoom. Some time * speed e ele rola. Para uma textura, gire em torno do
meio centralizando antes: rotate2d(-a) * (uv - 0.5) + 0.5, e amostre com o resultado. Aplicar uma transformação
diferente em cada pixel, vinda de uma onda ou de um ruído, é UV warping e distorção de domínio: a mesma ideia, com o
deslocamento variando pela tela.
Uma mat2 guarda rotação e escala, mas não translação, e é por isso que o deslocamento é somado à parte. Incluir a
translação na matriz exige mais uma dimensão, uma mat3 sobre vec3(p, 1.0); em 3D isso vira a mat4 das matrizes de
modelo, visão e projeção.