11. Transformações afins

Até agora discutimos elementos básicos da programação geométrica como pontos, vetores e suas operações, sistemas de coordenadas e como alterar a representação de pontos e vetores de um sistema para outro. Nessa aula vamos passar a discutir como mapear pontos de um lugar para outro. Por exemplo, suponha que você queira desenhar uma animação de uma bola girando. Como você definiria a função que mapeia cada ponto da bola para sua posição rotacionada por um determinado ângulo?

Consideraremos uma classe de transformações limitada, mas interessante, chamada de transformações afins. Estas incluem (entre outras) as seguintes transformações do espaço: translações, rotações, escalas uniformes e não uniformes (esticando os eixos por algum fator de escala constante), reflexões (invertendo objetos em torno de uma linha) e cisalhamento (que deforma quadrados em paralelogramos). Essas transformações estão ilustradas na Figura Fig. 11.1.

Transformações afins

Fig. 11.1 Exemplos de transformações afim.

Todas essas transformações da Figura Fig. 11.1 têm várias coisas em comum. Por exemplo, todos elas mapeiam linhas para linhas. Observe que algumas (translação, rotação e reflexão) preservam os comprimentos dos segmentos de linha e os ângulos entre os segmentos. Outras (como a escala uniforme) preservam os ângulos, mas não os comprimentos. Outras (como cisalhamento e escala não uniforme) não preservam ângulos nem comprimentos.

Todas as transformações listadas acima preservam combinações afins. Na verdade, esta é a definição de uma transformação afim. Por exemplo, dada qualquer transformação \(\mathbf{T}\) de uma das variedades acima, e dados dois pontos \(P\) e \(Q\), e qualquer escalar \(\alpha\), então:

\(R = (1-\alpha) P + \alpha Q \;\; \Rightarrow \;\; \mathbf{T}(R) = (1-\alpha)\mathbf{T}(P) + \alpha \mathbf{T}(Q)\)

De forma mais intuitiva, se \(R\) é ponto médio de um segmento \(\overline{PQ}\) antes de aplicar a transformação, então, após a transformação, \(\mathbf{T}(R)\) será o ponto médio do segmento \(\overline{\mathbf{T}(P)\mathbf{T}(Q)}\).

11.1. Representação matricial

Vamos nos concentrar no espaço 3D. Como consequência da invariância das relações afins temos que para:

\(R = \alpha_0 F.\vec{e}_0 + \alpha_1 F.\vec{e}_1 + \alpha_2 F.\vec{e}_2 + \alpha_3 \mathcal{O}\),

então:

\(\mathbf{T}(R) = \alpha_0 \mathbf{T}(F.\vec{e}_0) + \alpha_1 \mathbf{T}(F.\vec{e}_1) + \alpha_2 \mathbf{T}(F.\vec{e}_2) + \alpha_3 \mathbf{T}(\mathcal{O})\).

Nesse caso \(\alpha_3\) é 0 (zero para vetores) ou 1 (um para pontos). A equação da esquerda é a representação de um ponto ou vetor \(R\) em termos do sistema de coordenadas \(F\). Essa implicação mostra que se conhecemos a imagem dos elementos do sistema sob a transformação, então conhecemos a imagem \(R\) sob a transformação.

Vimos na aula anterior que a representação homogênea de coordenadas de \(R\) em relação ao sistema \(F\) é \(R[F] = (\alpha_0, \alpha_1, \alpha_2, \alpha_3)^T\). Lembre-se que o sobrescrito \(T\) neste contexto significa transpor este vetor linha para um vetor coluna, e não deve ser confundido com a transformação \(\mathbf{T}\). Assim, podemos expressar a relação acima em forma matricial como:

\(\mathbf{T}(R)[F] = \begin{pmatrix} \mathbf{T}(F.\vec{e}_0)[F] \;\; \mid \;\; \mathbf{T}(F.\vec{e}_1)[F] \;\; \mid \;\; \mathbf{T}(F.\vec{e}_2)[F] \;\; \mid \;\; \mathbf{T}(F.\mathcal{O})[F] \end{pmatrix} \begin{pmatrix}\alpha_0 \\ \alpha_1 \\ \alpha_2 \\ \alpha_3\end{pmatrix}\).

Nessa representação as colunas da matriz correspondem às imagens dos elementos do sistema sob \(\mathbf{T}\). Isso implica que aplicar uma transformação afim (na forma de coordenadas) é equivalente a multiplicar as coordenadas por uma matriz. Na dimensão \(d\) esta é uma matriz \((d + 1) \times (d + 1)\).

Se você achou tudo isso um pouco abstrato, no restante dessa aula vamos fornecer alguns exemplos concretos. Em vez de considerar isso no contexto de transformações bidimensionais, vamos considerá-lo no cenário mais geral de transformações tridimensionais. Os casos bidimensionais podem ser derivadas simplesmente ignorando as linhas e colunas para as coordenadas z.

11.2. Translação

Traslação por um vetor \(\vec{v}\) mapeia um ponto \(P\) para \(P + \vec{v}\).

Observe que, como os vetores não têm posição no espaço, os vetores livres não são alterados por translação.

Suponha que em relação ao sistema de coordenadas padrão, \(\vec{v}[F] = (\alpha_x,\alpha_y,\alpha_z,0)^T\) sejam as coordenadas homogêneas de \(\vec{}\). Os três vetores unitários não são afetados pela translação, e a origem é mapeada para \(\mathcal{O} + \vec{v}\), cujas coordenadas homogêneas são \((\alpha_x,\alpha_y,\alpha_z,1)\). Assim, pela regra dada anteriormente, a representação matricial homogênea para esta transformação de translação é

\(\mathbf{T}(\vec{v}) = \begin{pmatrix} 1 & 0 & 0 & \alpha_x \\ 0 & 1 & 0 & \alpha_y \\ 0 & 0 & 1 & \alpha_z \\ 0 & 0 & 0 & 1 \end{pmatrix}\).

11.3. Escala

O escalonamento uniforme é uma transformação que é realizada em relação a algum ponto fixo central. Vamos supor que este ponto é a origem do sistema de coordenadas padrão. (Vamos deixar o caso geral como exercício.) Dado um \(\beta\) escalar, essa transformação mapeia o objeto (ponto ou vetor) com coordenadas \((\alpha_x,\alpha_y,\alpha_z,\alpha_w)^T\) para \((\beta \alpha_x,\beta \alpha_y,\beta \alpha_z,\alpha_w)^T\).

Em geral, é possível especificar fatores de escala separados para cada um dos eixos. Isso é chamado de escalonamento não uniforme. Os vetores unitários são esticados pelo fator de escala correspondente e a origem não é movida.

A forma matricial mais genérica dessa transformação é

\(S(\beta_x, \beta_y, \beta_z) = \begin{pmatrix} \beta_x & 0 & 0 & 0 \\ 0 & \beta_y & 0 & 0 \\ 0 & 0 & \beta_z & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\).

Observe que tanto pontos quanto vetores são afetados por escalonamento.

11.4. Reflexão

Uma reflexão em 2D ao redor de uma linha mapeia os pontos refletindo-os em torno dessa linha. Uma reflexão em 3D recebe um plano e reflete os pontos no espaço em torno desse plano. Neste caso, a reflexão é apenas um caso especial de escalonamento por um fator de escala negativo. Por exemplo, para refletir pontos sobre o plano \(yz\), devemos escalar a coordenada \(x\) por -1. Essa matriz de reflexão é dada por:

\(F_x = \begin{pmatrix} -1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\).

Os demais casos para os planos \(xz\) e \(xy\) são similares. Uma reflexão ao redor de uma linha ou plano arbitrário fica como exercício.

11.5. Rotação

De forma geral, uma rotação é definida em torno de algum ponto fixo em 2D ou em torno de algum vetor fixo no espaço 3D. Consideraremos o caso mais simples em que o ponto fixo é a origem do sistema de coordenadas e o vetor é um dos eixos de coordenadas. Assim, em 3D existem três rotações básicas: sobre os eixos \(x\), \(y\) e \(z\). Em cada caso a rotação é definida por meio de um ângulo \(\theta\) dado em radianos. Vamos assumir que a rotação está de acordo com a regra da mão direita, ou seja, se o polegar direito estiver alinhado com o eixo de rotação, a rotação positiva será indicada pelos demais dedos.

Por exemplo, no plano \(xy\) (em 2D), para um sistema com eixo horizontal \(x\) apontando para a direita, \(y\) para cima e \(z\) saindo do plano (sistema de eixos de acordo com a mão direita) uma rotação positiva segue no sentido anti-horário. Ainda nesse caso, após uma rotação por um ângulo \(\theta\), o vetor \(z\) e a origem não se alteram, o vetor unitário em \(x\) é mapeado para \((cos(\theta), sin(\theta), 0, 0)^T\) e o vetor unitário em \(y\) é mapeado para \((-sin(\theta), cos(\theta), 0, 0)^T\). A matriz de rotação nesse caso é dada por:

\(R_z(\theta) = \begin{pmatrix} cos(\theta) & -sin(\theta) & 0 & 0 \\ sin(\theta) & cos(\theta) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\).

Observe que tanto pontos quanto vetores são afetados por rotação. Para rotações aplicadas aos demais eixos temos:

\(R_x(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & cos(\theta) & -sin(\theta) & 0 \\ 0 & sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\), \(R_y(\theta) = \begin{pmatrix} cos(\theta) & 0 & sin(\theta) & 0 \\ 0 & 1 & 0 & 0 \\ -sin(\theta) & 0 & cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\).

11.6. Cisalhamento

Uma transformação de cisalhamento é talvez a mais difícil de se visualizar. Pense em um cubo de gelatina. O que ocorre quando inclinamos esse cubo? Em 2D, um cisalhamento é uma transformação que mapeia um quadrado em um paralelogramo deslizando um lado paralelo a si mesmo enquanto mantém o lado oposto fixo. No espaço tridimensional, ele mapeia um cubo em um paralelepípedo deslizando uma face paralela enquanto mantém a face oposta fixa.

Consideraremos a forma mais simples, na qual começamos com um cubo unitário cujo canto inferior esquerdo coincide com a origem. Considere um dos eixos, digamos o eixo \(z\). A face do cubo que está no plano de coordenadas \(xy\) não se move. A face que se encontra no plano \(z = 1\) é transladada por um vetor \((h_x,h_y)\). Em geral, um ponto \(P = (P_x,P_y,P_z,1)\) é transladado pelo vetor \(P_z(h_x,h_y,0,0)\). Esse vetor é ortogonal ao eixo \(z\) e seu comprimento é proporcional à coordenada \(z\) de \(P\). Isso é chamado de cisalhamento \(xy\). (Os cisalhamentos \(yz\) e \(xz\) são definidos de forma análoga.)

Sob o cisalhamento \(xy\), a origem e os vetores unitários \(x\) e \(y\) permanecem inalterados. O vetor unitário \(z\) é mapeado para \((h_x , h_y , 1, 0)^T\). Assim, a matriz para esta transformação é:

\(H_{xy}(h_x, h_y) = \begin{pmatrix} 1 & 0 & h_x & 0 \\ 0 & 1 & h_y & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\).

Os cisalhamentos envolvendo outros pares de eixos são definidos de forma análoga:

\(H_{yz}(h_y, h_z) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ h_y & 1 & 0 & 0 \\ h_z & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\), \(H_{xz}(h_x, h_y) = \begin{pmatrix} 1 & h_x & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & h_z & 1 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\).

11.7. Onde estamos e para onde vamos?

Nessa aula começamos utilizar a forma matricial para representar transformações. Essa representação é bastante utilizada em computação gráfica por ser bastante compacta e eficiente. Na próxima aula, veremos como essas matrizes de transformação podem ser utilizadas no WebGL.

11.8. Exercícios

  1. Uma forma de se aplicar uma translação a um conjunto de vértices no WebGL é usando um uniforme do tipo vec2. Por exemplo, as posições do buffer aPosition podem ser transladadas por um uniforme uniform vec2 uTranslacao simplesmente usando a aritmética de vec2 do próprio GLSL como aPosition + uTranslation, para calcular as posições transladadas.

    Considere a animação de uma única bola, simplificando esse exemplo no JSitor. Modifique o vertex shader para incluir um uniforme in vec2 uTranslacao que calcula a posição transladada e altere também o restante do programa de animação para usar a translação diretamente no shader ao invés de recalcular os vértices.

  2. Suponha que você tenha a sua disposição uma função desenheQuadrado(Rz, Sx, Sy, Tx, Ty, color) que desenha um quadrado de cor color, de tamanho \(1 \times 1\) no sistema de coordenadas normalizadas de recorte do WebGL, rotacionado de Rz, escalonado por (Sx, Sy) e transladado para (Tx, Ty) em um canvas de resolução W \(\times\) H. Esboce, em pseudo-linguagem, como desenhar a figura abaixo:

exercício 2

Fig. 11.2 Como desenhar essa figura usando a função desenheQuadrado()?

11.9. Para saber mais