Pular para o conteúdo
Fundamento · CoordenadasIniciante

Coordenadas polares

Descreva um ponto pelo ângulo e pela distância até um centro, para tudo que é radial.

Como funciona

As coordenadas UV descrevem um ponto pelo quanto ele está à direita e para cima. As coordenadas polares descrevem o mesmo ponto pela distância até um centro e pela direção: um raio e um ângulo. Tudo que se repete em volta de um centro, como pétalas, raios de luz, um ponteiro de relógio, um redemoinho ou a varredura de um radar, é fácil de escrever na forma polar e desajeitado em x e y.

De x, y para ângulo e raio

Comece de uma coordenada p centralizada e com a proporção corrigida, com o centro em (0, 0). O raio é o comprimento do vetor, e o ângulo vem do 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.

Leitura complementar