8.2 Projeção perspectiva
Na projeção perspectiva, quanto mais distantes os objetos estiverem do centro de projeção, menor ficarão quando projetados. Isso produz o efeito de diminuição de tamanho de objetos distantes, que é o que percebemos no mundo real. A figura 8.8 mostra esse efeito em uma fotografia. Note como os elementos da cena parecem convergir em um ponto distante. Esse ponto de convergência é chamado de ponto de fuga.
).](https://upload.wikimedia.org/wikipedia/commons/6/6b/One_point_perspective.jpg)
Figura 8.8: Diminuição de tamanho na projeção perspectiva (fonte).
O número de pontos de fuga é determinado pela orientação da câmera em relação a um objeto cuboide referencial no espaço do mundo (figura 8.9).
Figura 8.9: Pontos de fuga na projeção perspectiva.
Se o cubo tiver arestas paralelas aos eixos e da câmera, a projeção terá 1 ponto de fuga. Se o cubo tiver arestas paralelas apenas em relação a um dos eixos ( ou ), a projeção terá 2 pontos de fuga. Se o cubo não tiver arestas paralelas aos eixos e , a projeção terá 3 pontos de fuga.
Para produzir uma matriz de projeção perspectiva, adotaremos a mesma estratégia de normalizar o volume de visão, isto é, criaremos uma transformação que converte um volume de visão no espaço da câmera para o volume de visão de tamanho no espaço NDC. Entretanto, dessa vez o volume de visão terá o formato de uma pirâmide truncada (chamada de view frustum), como mostra a figura 8.10.
Figura 8.10: Volume de visão genérico para projeção perspectiva.
O volume de visão possui um formato piramidal pois todos os pontos do volume estão sobre projetores que convergem em direção à origem do espaço da câmera, que é o centro de projeção. O formato da pirâmide é definido unicamente pelos parâmetros (left), (right), (bottom), (top), (near) e (far).
Suponha a cena de um arranjo de 8 cubos conforme mostra a figura 8.11.
Figura 8.11: Cena dentro do volume de visão de projeção perspectiva.
Após a normalização do volume de visão, todo o seu conteúdo é distorcido proporcionalmente como mostra a figura 8.12. Observe como os objetos mais distantes ficam menores em relação aos objetos mais próximos, e como as arestas laterais dos cubos não são mais paralelas como na cena original. De fato, elas agora convergem para um ponto de fuga.
Figura 8.12: Distorção da cena após a normalização do volume de visão.
Agora que a geometria da cena está distorcida, podemos seguir com o processamento do pipeline de gráfico. Após a rasterização e o mapeamento ortogonal para o espaço da janela, o resultado será uma imagem que tem a aparência de uma projeção perspectiva (figura 8.13).
Figura 8.13: Objetos em NDC e conteúdo correspondente no espaço da janela.
Matriz de projeção
Para construir a matriz a projeção perspectiva, vamos observar primeiro como um ponto no espaço da câmera (o subscrito vem de eye space) é projetado para um ponto no plano de recorte próximo (isto é, o plano com ).
A figura 8.14 mostra a relação entre esses pontos em uma visão de cima do volume de visão.
Figura 8.14: Volume de visão visto de cima.
Através da razão entre triângulos semelhantes, temos
Logo,
O mesmo raciocínio pode ser aplicado para determinar . A figura 8.15 mostra uma visão lateral do volume de visão.
Figura 8.15: Volume de visão visto de lado.
Através da razão entre triângulos semelhantes,
Logo,
O importante a ser notado aqui é que tanto quanto são divididos por . Então, todo ponto no espaço da câmera deverá ser dividido pela sua coordenada negativa.
Podemos incorporar a divisão por na matriz de projeção. Lembre-se que, no vertex shader, representamos pontos e vetores em coordenadas homogêneas. A matriz de projeção converte coordenadas homogêneas do espaço da câmera (, , , ) em coordenadas homogêneas do espaço de recorte (, , , ), que são as coordenadas de gl_Position
:
Após o recorte, as coordenadas do espaço de recorte são divididas por para produzir coordenadas (, , ) no espaço NDC:
Aproveitando essa divisão por , podemos obter a divisão por através da mudança da última linha da matriz de projeção, como a seguir:
Observe que . Portanto, as coordenadas serão divididas por como desejamos.
Da mesma forma como fizemos para normalizar o volume de visão da projeção ortográfica, sabemos que precisamos mapear os intervalos:
- Em : , no espaço da câmera, para em NDC;
- Em : , no espaço da câmera, para em NDC;
- Em : , no espaço da câmera, para em NDC.
Os fatores de translação e escala em e em são os mesmos da projeção ortográfica. Assim, temos a seguinte relação entre coordenadas em NDC e coordenadas projetadas :
onde e são, respectivamente, os fatores de escala e translação:
Se substituirmos
na expressão
obtemos a relação final entre a coordenada do espaço da câmera e a coordenada no espaço NDC (o mesmo raciocínio pode ser aplicado para a transformação de em ):
onde
De forma semelhante,
onde
Atualizando os elementos da matriz de projeção,
Ainda precisamos determinar os elementos da terceira linha da matriz. Esses elementos correspondem à transformação de em .
O valor de não depende de e . Assim, os valores nas duas primeiras colunas da terceira linha devem ser zero. Só precisamos determinar os elementos da terceira e quarta colunas, que chamaremos de e :
Logo,
Após a divisão pelo ,
Sabendo que o intervalo deve ser mapeado para o intervalo , podemos formar um sistema de equações lineares:
Logo,
Com isso obtemos todos os elementos da matriz de projeção perspectiva:
Na biblioteca GLM, tal matriz pode ser criada com a função glm::frustum
definida em glm/gtc/matrix_transform.hpp
:
float left, float right, float bottom, float top, float zNear, float zFar);
glm::mat4 glm::frustum(double left, double right, double bottom, double top, double zNear, double zFar); glm::dmat4 glm::frustum(
onde left
, right
, bottom
, top
, zNear
e zFar
correspondem respectivamente aos valores , , , , e .
Se o volume de visão for simétrico, então
Assim como na projeção ortográfica com volume de visão simétrico, os termos da matriz podem ser simplificados como segue:
e a matriz é simplificada para
Uma forma mais intuitiva de criar um volume de visão simétrico para a projeção perspectiva é através dos seguintes parâmetros:
- Ângulo de abertura vertical do campo de visão (field of view ou FOV).
- Razão de aspecto (largura pela altura) do plano de imagem.
- Distâncias e dos planos de recorte próximo (near) e distante (far).
Usando relações trigonométricas, podemos determinar o valor de (top
em glm::frustum
) (figura 8.16):
Por simetria,
Figura 8.16: Ângulo de abertura do campo de visão vertical.
Para calcular (right
em glm::frustum
), multiplicamos pela razão de aspecto.
Assim, em um viewport de tamanho , a razão de aspecto será (widescreen). Se , então .
Por simetria,
Na biblioteca GLM, tal matriz pode ser criada com a função glm::perspective
, definida em glm/gtc/matrix_transform.hpp
:
float fovy, float aspect, float zNear, float zFar);
glm::mat4 perspective(double fovy, double aspect, double zNear, double zFar); glm::dmat4 perspective(
onde fovy
, aspect
, zNear
e zFar
correspondem respectivamente aos valores (em radianos), , e .