8.1 Projeção ortográfica

Por padrão, o pipeline do OpenGL produz uma projeção ortográfica das primitivas contidas no volume de visão, como se as coordenadas z de todos os pontos dentro do volume de visão fossem descartadas para formar figuras no plano de imagem.

Lembre-se que o volume de visão é um cubo 2×2×2 centralizado na origem no espaço normalizado do dispositivo (NDC). Logo antes da rasterização, o pipeline converte automaticamente as coordenadas do NDC para o espaço da janela, em pixels.

Na configuração padrão de glViewport(x, y, w, h) e glDepthRange(n, f), o canto inferior esquerdo da janela é a origem do espaço da janela (x=y=0), e o canto superior direito é a coordenada (w,h) onde w e h correspondem respectivamente à largura e altura da janela em pixels29. Assim, o seguinte mapeamento é feito internamente pelo pipeline:

  • xndc[1,1] em NDC torna-se xw[x,w] no espaço da janela.
  • yndc[1,1] em NDC torna-se yw[y,h] no espaço da janela.
  • zndc[1,1] em NDC torna-se zw[n,f] no espaço da janela.

O mapeamento pode ser representado pela seguinte matriz de viewport:

[xwywzw]=[w200x+w20h20y+h200fn2f+n2][xndcyndczndc1]. Por padrão, n=0 e f=1 (configuração padrão de glDepthRange).

Após o mapeamento para o espaço da janela, as primitivas são rasterizadas. Se mais de um fragmento for mapeado para o mesmo pixel no framebuffer, o teste de profundidade pode ser utilizado para manter apenas o fragmento de menor valor zw.

A figura 8.5 mostra um exemplo no qual o espaço NDC contém 8 cubos posicionados em (±0.5,±0.5,±0.5), alinhados aos eixos principais. Após a rasterização com o teste de profundidade habilitado na configuração padrão, o conteúdo rasterizado no espaço da janela exibirá apenas a face da frente dos 4 cubos de menor valor z, como ocorreria numa projeção ortográfica sobre o plano z=1 em NDC. A figura também mostra que a origem do espaço NDC (Ondc) é mapeada para o centro da janela.

Objetos em NDC e conteúdo correspondente no espaço da janela.

Figura 8.5: Objetos em NDC e conteúdo correspondente no espaço da janela.

Uma vez que o pipeline do OpenGL usa a projeção ortográfica como padrão, podemos supor inicialmente que nossa matriz de projeção é a matriz identidade, isto é, nenhuma transformação adicional precisa ser feita para produzir a projeção ortográfica. Assim, no vertex shader, as coordenadas serão modificadas apenas pelas matrizes de modelo e visão (MviewMmodel), gerando pontos no espaço da câmera. Relembre que, no espaço da câmera, a posição da câmera é o ponto de referência do frame, e a direção de visão é a direção do eixo z negativo.

A matriz de projeção é responsável por converter coordenadas do espaço da câmera para o espaço de recorte. Entretanto, como estamos supondo que a matriz de projeção é a matriz identidade, o espaço de recorte é, neste caso, idêntico ao espaço da câmera. Então, os pontos no espaço da câmera podem ser enviados diretamente à variável embutida gl_Position.

Internamente, o pipeline supõe que, no espaço de recorte, todas as primitivas com coordenadas menores que w e maiores que +w devem ser recortadas. Uma vez que w=1 para todos os pontos, são recortadas todas as primitivas que estiverem fora do cubo que vai de (1,1,1) até (1,1,1). Após o recorte, as coordenadas são divididas por w para converter coordenadas do espaço de recorte para coordenadas normalizadas do dispositivo. Mas, como w=1, as coordenadas continuam com o mesmo valor. Então, neste caso, o espaço NDC é idêntico ao espaço de recorte projetado, que por sua vez é idêntico ao espaço da câmera.

Há um problema em usar a matriz identidade como matriz de projeção: consideramos até agora que os modelos geométricos são representados em um sistema que segue a regra da mão direita. Entretanto, as coordenadas normalizadas do dispositivo seguem a regra da mão esquerda. Isso faz com que orientação dos triângulos fique invertida (CW vira CCW e vice-versa). Felizmente, é fácil construir uma transformação que converte as coordenadas para a regra da mão direita: basta negarmos a coordenada z de cada ponto. Essa transformação pode ser feita por uma matriz de projeção ligeiramente diferente de uma matriz identidade:

Morth=[1000010000100001].

Agora, no vertex shader, gl_Position receberá os pontos transformados por MprojMviewMview, onde Mproj=Morth.

Com a matriz Morth conseguimos consertar a inversão de orientação das primitivas. No entanto, há ainda outro problema: como a câmera está na origem em NDC, só conseguimos enxergar na tela a geometria que estiver contida no cubo de tamanho 2 em torno da câmera (isto é, a geometria após o recorte). Esse tamanho é muito limitante para a maioria das cenas. Além disso, a posição da câmera no centro do cubo não parece ser algo muito intuitivo. Na câmera LookAt, a direção de visão é a direção de z negativo. Logo, não deveríamos ser capazes de enxergar algo que está com z positivo (isto é, atrás da câmera, ainda que dentro do cubo de tamanho de 2). Para resolver isso, vamos criar uma nova matriz de projeção que supõe que o volume de visão está sempre situado em algum lugar do espaço da câmera com z<0, como ilustra a figura 8.6.

Volume de visão genérico para projeção ortográfica.

Figura 8.6: Volume de visão genérico para projeção ortográfica.

Nessa figura, o lado mais perto do volume de visão está a uma distância n da câmera, medida ao longo de sua linha de visão (eixo z negativo). Esse lado mais próximo em relação à posição da câmera é chamado de plano de recorte próximo ou near clipping plane. O lado mais distante do volume de visão está a uma distância f da câmera, e é chamado de plano de recorte distante ou far clipping plane.

Na definição desse novo volume de visão, usaremos parâmetros l (left), r (right), b (bottom), t (top) para especificar a posição dos lados esquerdo e direito, de baixo e de cima do volume. Desse modo, o volume não precisará ser mais um cubo de tamanho 2 em cada direção. Podemos obter essa configuração através da modificação da matriz de projeção.

É interessante notar que a matriz de projeção ortográfica é simplesmente uma matriz que transforma o volume de visão, do espaço da câmera, para o volume de visão em NDC, como mostra a figura 8.7.

A matriz de projeção ortográfica representa a transformação do volume de visão do espaço da câmera para o volume de visão em NDC.

Figura 8.7: A matriz de projeção ortográfica representa a transformação do volume de visão do espaço da câmera para o volume de visão em NDC.

Esse mapeamento da transformação de projeção consiste em fazer com que os pontos (l,b,n) e (r,t,f) no espaço da câmera tornem-se respectivamente os pontos (1,1,1) e (1,1,1) em NDC. Tal processo é chamado de normalização do volume de visão. Podemos fazer a normalização em três etapas:

  1. Translação do volume de visão de modo a centralizá-lo na origem.
  2. Escala do volume de visão de modo a deixá-lo com tamanho 2 em cada direção.
  3. Reflexão para inverter a coordenada z.

Como a reflexão é uma escala com inversão de sinal, as etapas 2 e 3 podem ser feitas em conjunto, como veremos a seguir.

Translação

O centroide C=(cx,cy,cz) do volume de visão no espaço da câmera é

cx=r+l2,cy=t+b2,cz=f+n2.

Logo, a matriz de translação que desloca o volume de visão para a origem é a matriz de translação por C:

T=[100r+l2010t+b2001f+n20001].

Escala e reflexão

Os fatores de escala S=(sx,sy,sz) para redimensionar o volume de visão em um cubo com tamanho 2 em cada direção são:

sx=2rl,sy=2tb,sz=2fn. Entretanto, precisamos refletir o cubo na direção z para a conversão da regra da mão direita para mão esquerda. Assim, precisamos inverter o sinal de sz:

sz=2fn. A matriz de escala ficará como a seguir:

S=[2rl00002tb00002fn00001].

Matriz de projeção

Concatenando as transformações de translação, escala e reflexão, obtemos a nova matriz de projeção ortográfica:

Morth=ST=[2rl00002tb00002fn00001][100r+l2010t+b2001f+n20001],Morth=[2rl00r+lrl02tb0t+btb002fnf+nfn0001].

Na biblioteca GLM, tal matriz pode ser criada com a função glm::ortho, definida em glm/gtc/matrix_transform.hpp:

glm::mat4 glm::ortho(float left, float right, float bottom, float top, float zNear, float zFar);
glm::dmat4 glm::ortho(double left, double right, double bottom, double top, double zNear, double zFar);

onde left, right, bottom, top, zNear e zFar correspondem respectivamente aos valores l, r, b, t, n e f.

Na maioria das aplicações, trabalhamos com volumes simétricos nas direções x e y. Nesse caso, os pontos (0,0,z) em NDC são projetados no centro do viewport. Em um volume simétrico,

r=l,t=b.

Com isso, os termos da matriz anterior podem ser simplificados como segue:

r+l=0,rl=2r,t+b=0,tb=2t,

e a matriz assume o fomato

Morth=[1r00001t00002fnf+nfn0001].


  1. A configuração padrão é a configuração utilizada em todos os projetos da ABCg feitos até agora.↩︎