Pular para o conteúdo
Fundamento · CoordenadasIntermediário

Espaços de coordenadas

Espaço do objeto, do mundo, da câmera, de recorte e da tela, e por que toda posição e direção precisa dizer em qual deles está.

Como funciona

Um vec3 chamado position são três números. Se (0, 1, 0) quer dizer “o topo do modelo”, “um metro acima da origem do mundo” ou “logo acima da câmera” depende do espaço de coordenadas em que ele é medido. A maioria dos bugs de iluminação vem de misturar dois valores que vivem em espaços diferentes: a conta roda, o compilador fica feliz e a imagem sai sutilmente errada. Dê nome ao espaço de cada posição e direção que você usa, e só combine valores que estejam no mesmo.

A cadeia

Um vértice passa por uma sequência fixa de espaços, uma matriz de cada vez:

  1. Espaço do objeto (também chamado de local ou do modelo): as coordenadas como o artista modelou, relativas à origem do próprio modelo. Os vértices de uma esfera ficam em volta de (0, 0, 0), não importa onde a esfera esteja.
  2. Espaço do mundo: o referencial comum da cena. A matriz de modelo posiciona, gira e escala o objeto nele. Luzes, câmera e outros objetos costumam ser descritos aqui, o que faz dele o espaço comum para iluminação.
  3. Espaço da câmera (ou de visão): o mundo visto da câmera. A matriz de visão, a inversa da transformação da própria câmera, move tudo para que a câmera fique na origem. Na convenção do OpenGL ela olha para -z.
  4. Espaço de recorte (clip space): a matriz de projeção leva o volume de visão da câmera para uma caixa e coloca a profundidade num quarto componente, w. O trabalho do shader de vértice termina aqui: gl_Position está no espaço de recorte.
  5. Coordenadas normalizadas de dispositivo (NDC): a GPU divide por w (a divisão de perspectiva), o que deixa as coisas distantes menores. Os pontos visíveis acabam entre -1 e 1 em x e y. Em z, OpenGL e WebGL usam de -1 a 1; Direct3D, Vulkan e Metal usam de 0 a 1.
  6. Espaço da tela (ou da janela): a transformação de viewport escala as NDC para pixels. É isso que gl_FragCoord.xy guarda no shader de fragmento, com (0.5, 0.5) no centro do pixel do canto inferior esquerdo no OpenGL e no WebGL.
// shader de vértice: entra espaço do objeto, sai espaço de recorte, a posição no mundo segue adiante
uniform mat4 uModel;
uniform mat4 uView;
uniform mat4 uProjection;
in vec3 aPosition;      // espaço do objeto
out vec3 vWorldPos;     // espaço do mundo, interpolado para o shader de fragmento

void main() {
    vec4 world = uModel * vec4(aPosition, 1.0);
    vWorldPos = world.xyz;
    gl_Position = uProjection * uView * world;
}

As matrizes se aplicam da direita para a esquerda: a que está ao lado do vetor age primeiro. As matrizes em si têm um fundamento próprio; aqui o que importa é em que espaço cada valor termina.

Pontos e direções

Uma posição tem um lugar; uma direção só tem uma orientação. No quarto componente, um ponto recebe w = 1.0 e uma direção recebe w = 0.0, então uma matriz 4×4 move os pontos mas só gira e escala as direções. Uma direção de luz multiplicada com w = 1.0 herda a translação e aponta para o lugar errado. As normais precisam de mais uma correção sob escala não uniforme, explicada em normais.

Escolhendo um espaço

A iluminação funciona em qualquer espaço, desde que todas as entradas estejam nele. Escolha o espaço onde a maioria das suas entradas já está:

// shader de fragmento, tudo no espaço do mundo
uniform vec3 uCameraPos;     // espaço do mundo
uniform vec3 uLightDir;      // espaço do mundo, em direção à luz
in vec3 vWorldPos;
in vec3 vWorldNormal;

// ...
vec3 n = normalize(vWorldNormal);
vec3 v = normalize(uCameraPos - vWorldPos); // da superfície em direção à câmera
float facing = dot(n, v);

Os cards deste site seguem essa escolha: i.position, i.normal e i.view estão todos no espaço do mundo, então podem ser combinados direto. As engines nem sempre concordam. No fragment() de um shader spatial do Godot, VERTEX, NORMAL e VIEW estão no espaço da câmera; multiplique por INV_VIEW_MATRIX (com w = 1.0 para a posição e 0.0 para as direções) para levá-los ao espaço do mundo antes de misturá-los com dados do mundo.

O espaço do objeto é a escolha certa para efeitos que devem acompanhar o modelo quando ele se move, como um padrão pintado num objeto que gira. O espaço do mundo serve para efeitos presos à cena, como uma linha de varredura que passa por tudo ou neve nas faces voltadas para cima. O espaço da tela serve para efeitos presos à imagem, como o pós-processamento.

Leitura complementar