Coordenadas polares
Descreva um ponto pelo ângulo e pela distância até um centro, para tudo que é radial.
Como funciona
As coordenadas 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. 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.uv = gl_FragCoord.xy / resolution
De x, y para ângulo e raio
Comece de uma coordenada Vetor Um grupo de dois a quatro números tratado como um só valor (vec2, vec3, vec4). O mesmo tipo guarda uma posição, uma direção, uma cor ou uma UV.p centralizada e com a proporção corrigida, com o centro em (0, 0). O raio é o comprimento
do vetorUm grupo de dois a quatro números tratado como um só valor (vec2, vec3, vec4). O mesmo tipo guarda uma posição, uma direção, uma cor ou uma UV.atan com dois argumentos:
float r = length(p); // 0 no centro
float a = atan(p.y, p.x); // -PI..PI, 0 apontando para a direita (+x), sentido anti-horário
O atan(y, x) de dois argumentos olha o sinal dos dois componentes, então sabe em qual quarto do círculo o ponto está
e devolve a volta inteira, de -π a π. O atan(p.y / p.x) de um argumento só enxerga a razão, não distingue direções
opostas, devolve de -π/2 a π/2 e divide por zero no eixo vertical. No HLSL a forma de dois argumentos se chama
atan2, na mesma ordem: y primeiro. Bem no centro, onde os dois são zero, o resultado no GLSL é indefinido; quase
nunca importa, porque ali o raio é zero de qualquer forma.
Um ângulo em radianos é difícil de passar para outras funções, então remapeie para 0..1:
const float PI = 3.14159265;
float t = a / (2.0 * PI) + 0.5; // 0..1 numa volta, começando no eixo -x
Formas a partir de ângulo e raio
Na forma polar, um círculo é “raio abaixo de uma constante”. Faça o limite depender do ângulo e o círculo ganha pétalas:
#version 300 es
precision highp float;
uniform vec2 uResolution;
out vec4 fragColor;
void main() {
vec2 p = (2.0 * gl_FragCoord.xy - uResolution) / uResolution.y;
float r = length(p);
float a = atan(p.y, p.x);
float edge = 0.55 + 0.2 * cos(5.0 * a); // cinco pétalas
float flower = 1.0 - smoothstep(edge - 0.01, edge + 0.01, r);
fragColor = vec4(vec3(flower), 1.0);
}
O número inteiro em cos(5.0 * a) define quantas pétalas há. Ele precisa ser inteiro: o ângulo salta de π para -π no
lado esquerdo, e só um número inteiro de repetições se encaixa nesse salto. Um número fracionário deixa uma emenda
visível ali.
De volta para x e y
A volta é r * vec2(cos(a), sin(a)). O padrão útil é: converter, mudar o ângulo ou o raio, converter de volta e usar
o resultado como uma UV nova. Somar ao ângulo uma quantidade que cresce em direção ao centro torce a imagem num
redemoinho:
float twist = 2.0 * (1.0 - smoothstep(0.0, 1.0, r)); // mais forte no centro
vec2 q = r * vec2(cos(a + twist), sin(a + twist)); // amostre ou desenhe com q no lugar de p
A emenda
O ângulo é descontínuo no eixo -x, onde salta de π para -π (ou t de 1 para 0). Qualquer coisa que dependa do ângulo
diretamente, como uma consulta de textura com t como coordenada, pode mostrar uma linha ali. Padrões com um número
inteiro de repetições a escondem, como acima. Consultas de textura sobre a emenda pedem mais cuidado, porque a GPU
estima o quanto a coordenada muda entre pixels vizinhos, e o salto torna essa estimativa enorme; o fundamento de
derivadas em espaço de tela explica por que e como contornar.