O pipeline gráfico é um dos conceitos essenciais da Computação Gráfica, é importante entendê-lo para dominar as APIs gráficas OpenGL e DirectX, pois diversas funções destas API's agem sobre diferentes estágios do pipeline e a compreensão das etapas do pipelines farão o usuário ter uma melhor experiência ao utilizar estas API's.
Através da implementação de um pipeline gráfico, iremos compreender melhor cada um dos processos que ocorrem até que os modelos geométricos cheguem até a tela. Ainda conheceremos com mais detalhes as dificuldades que ocorrem entre as transições e que estratégias podem ser utilizadas para superar tais dificuldades. Com estes conhecimentos, iremos ser capazes de conhecer com mais clareza as ferramentas gráficas que temos atualmente e acrescentar uma melhor experiência ao utilizá-las.
O pipeline gráfico consiste exatamente de uma sequência de passos necessários para transformar uma descrição geométrica/matemática de uma cena 3D em uma descrição visual na tela 2D. A imagem final é obtida por meio da rasterização das primitivas projetadas na tela. Basicamente, cada passo do pipeline gráfico consiste de uma transformação geométrica de um sistema de coordenadas (espaço) para outro, são eles :
Para a parte de rasterização será utilizado a implementação referente ao trabalho 1 com algumas alterações para o uso no trabalho 2.
Esta etapa do pipeline é responsável por transformar vértices, originalmente descritos no espaço do objeto, para o espaço do universo. Isto é feito através da multiplicação destes vértices por uma matriz denominada matriz de modelagem (ou model matrix). A matriz de modelagem é composta por uma sequência de transformações geométricas que posicionam o modelo no universo. As transformações que podem compor a matriz de modelagem são a rotação, translação, escala e o cisalhamento (shear).
As rotações podem ser feitas nos três eixos de coordenadas, portanto seria necessário criar uma matriz para cada um dos tipos de rotação. Contudo, analisando a rotação em um dois eixos, é possível com algumas mudanças determinar as equações que irão rotacionar os vértices nos outros eixos.
Mas também podemos fazer isto para todos os eixos, misturando as rotações, rotacionando um objeto um pouco no eixo Y, depois em X e depois em Z.
Para calcular as novas posições dos vertices, também podemos escrever o valor de x de um vértice, pela distância dele até a origem multiplicada pelo cosseno do ângulo formado entre o vetor e o eixo X. O mesmo ocorre com o y de um vértice, que pode ser escrito pela distância multiplicada pelo seno do ângulo.
Agora iremos rotacionar os vértices que desejamos:
Os calculos são os seguintes:
E por fim teremos os resultados:
No codigo faremos o seguinte para a rotação em X:
E obteremos:
Em Y segue os mesmos passos:
E obteremos:
Para rotacionar em ambos, primeiro rotacionamos no eixo X e dps no eixo Y assim:
E obteremos:
Escalonar significa redimensionar um objeto, ou seja, aumentar ou diminuir suas dimensões, deformá-lo ou até mesmo espelhá-lo. Para fazer isso, basta multiplicar os valores das coordenadas de um vértice por um fator de escala - lembrando que as coordenadas de um vértice possuem valores inteiros para X, Y e Z no modelo 3D e para X e Y no modelo 2D. Essa operação de multiplicação deve ser feita para todos os vértices do objeto. Ao longo da postagem, vamos adotar a seguinte notação : vetor transformado = matriz de transformação x vetor original. Cada vetor é uma generalização para os vértices do objeto.
Matriz de escala:
Aplicando a escala no eixo Y, aumentando 3 vezes:
Uma outra forma de deformar os objetos de uma cena seria aplicando esta transformação. O shear mantém uma coordenada U fixa enquanto muda a coordenada V ao longo de seu eixo, ou seja, há uma relação linear entre V e U.
Matriz de Cisalhamento:
Aplicando cisalhamento no eixo X:
Aqui é definido como a cena será vista, então, faz-se necessário configurar a câmera. A câmera possui 3 dados importantes que devem ser descritos:
- Posição da Câmera: Local onde ela se encontra
- Direction: Local para onde a câmera está "olhando"
- Up: Fixa a câmera no eixo determinado
Assim, é criado o código que representa a Criação da Câmera:
Abaixo, é feito a parte que corresponde a essa etapa, o vertice, que agora possui os dados da etapa anterior, é multiplicado com a matriz view, criada para ser usada nessa etapa:
Inicialização da camera:
Até agora já percorremos metade do pipeline gráfico. Esta terceira etapa consiste em transformar vértices do espaço da câmera para o espaço de recorte (ou projetivo) e mais uma vez iremos construir uma matriz, a matriz de projeção (projection matrix). Dada a seguinte situação :
É interessante ressaltar que é nessa etapa onde ocorre a distorção perspectiva - objetos mais próximos do view plane aparentam ser maiores do que objetos que estão mais distantes.
Assim, é criado o código que representa a Matriz de Projeção:
Criação do View Plane - Matriz Projection:
Com a matriz de projeção criada, multiplica-se agora com resultado da etapa anterior, da seguinte forma:
Esta etapa do pipeline é responsável por transformar pontos do espaço de recorte para o espaço canônico (ou NDC – Normalized Device Coordinates). Isto é feito em duas etapas. Primeiro, dividem-se as coordenadas dos vértices no espaço de recorte pela sua coordenada homogênea. Esta transformação gera uma alteração da geometria da cena, tornando menores os objetos que estiverem mais distantes da câmera. Adicionalmente, esta transformação mapeia o volume de visualização (view frustum), com formato piramidal, em um hexahedro. A seguir, os vértices são multiplicados por uma matriz adicional que, ao aplicar escalas e translações, transforma estes vértices para o espaço canônico, transformando o hexahedro anterior em um cubo de coordenadas unitárias e centrado na origem.
Aqui ocorre a homogeneização, que no caso, significa dividir todos os componentes do vértice pela sua coordenada homogênea. Então, para fazer a divisão, fazemos o seguinte:
Como pode notar no código, é pego o último elemento, que é a coordenada homogênea, e assim, dividimos pelo resultado da etapa anterior, terminando essa etapa.
Esta etapa do pipeline é responsável por transformar pontos do espaço canônico para o espaço de tela. Isto é feito através da multiplicação dos vértices por uma matriz específica que envolve escalas e translações.
Aqui é utilizado o viewport, onde aqui os vértices do espaço canônico é mapeado para o espaço da tela. Para isso ocorrer, deve-se lembrar que os valores são arredondados, pois a tela possui apenas coordenadas inteiras.
Então, eis o código que representa a Matriz Viewport:
Criação da Tela - Matriz Viewport
Com a viewport criada, ocorre a multiplicação da etapa anterior com a viewport:
Esta parte consiste em mostrar como foi juntar os trechos anteriores em apenas uma função, comentando qual etapa corresponde a qual linha do código. Para isso, veja o código a seguir:
Aqui poderemos comparar o resultado obtido com o resultado do loader disponibilizado pelo professor.
Loader do professor:
Gerado pelo Pipeline:
Com a implementação do pipeline podemos colocar em prática os conhecimentos obtidos na disciplina e entender melhor o funcionamento.
- Notas de Aula do Professor Christian A. P.