.. _sec-transformacoes-afim: 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 :numref:`fig-afim`. .. figure:: ./figuras/a11/transformacoes.png :alt: Transformações afins :name: fig-afim :width: 90.0% :align: center Exemplos de transformações afim. Todas essas transformações da Figura :numref:`fig-afim` 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 :math:`\mathbf{T}` de uma das variedades acima, e dados dois pontos :math:`P` e :math:`Q`, e qualquer escalar :math:`\alpha`, então: :math:`R = (1-\alpha) P + \alpha Q \;\; \Rightarrow \;\; \mathbf{T}(R) = (1-\alpha)\mathbf{T}(P) + \alpha \mathbf{T}(Q)` De forma mais intuitiva, se :math:`R` é ponto médio de um segmento :math:`\overline{PQ}` **antes** de aplicar a transformação, então, após a transformação, :math:`\mathbf{T}(R)` será o ponto médio do segmento :math:`\overline{\mathbf{T}(P)\mathbf{T}(Q)}`. Representação matricial ----------------------- Vamos nos concentrar no espaço 3D. Como consequência da invariância das relações afins temos que para: :math:`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: :math:`\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 :math:`\alpha_3` é 0 (zero para vetores) ou 1 (um para pontos). A equação da esquerda é a representação de um ponto ou vetor :math:`R` em termos do sistema de coordenadas :math:`F`. Essa implicação mostra que se conhecemos a imagem dos elementos do sistema sob a transformação, então conhecemos a imagem :math:`R` sob a transformação. Vimos na aula anterior que a representação homogênea de coordenadas de :math:`R` em relação ao sistema :math:`F` é :math:`R[F] = (\alpha_0, \alpha_1, \alpha_2, \alpha_3)^T`. Lembre-se que o sobrescrito :math:`T` neste contexto significa transpor este vetor linha para um vetor coluna, e não deve ser confundido com a transformação :math:`\mathbf{T}`. Assim, podemos expressar a relação acima em forma matricial como: :math:`\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 :math:`\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 :math:`d` esta é uma matriz :math:`(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. Translação ----------- Traslação por um vetor :math:`\vec{v}` mapeia um ponto :math:`P` para :math:`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, :math:`\vec{v}[F] = (\alpha_x,\alpha_y,\alpha_z,0)^T` sejam as coordenadas homogêneas de :math:`\vec{}`. Os três vetores unitários não são afetados pela translação, e a origem é mapeada para :math:`\mathcal{O} + \vec{v}`, cujas coordenadas homogêneas são :math:`(\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 é :math:`\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}`. 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 :math:`\beta` escalar, essa transformação mapeia o objeto (ponto ou vetor) com coordenadas :math:`(\alpha_x,\alpha_y,\alpha_z,\alpha_w)^T` para :math:`(\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 é :math:`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. 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 :math:`yz`, devemos *escalar* a coordenada :math:`x` por -1. Essa matriz de reflexão é dada por: :math:`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 :math:`xz` e :math:`xy` são similares. Uma reflexão ao redor de uma linha ou plano arbitrário fica como exercício. 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 :math:`x`, :math:`y` e :math:`z`. Em cada caso a rotação é definida por meio de um ângulo :math:`\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 :math:`xy` (em 2D), para um sistema com eixo horizontal :math:`x` apontando para a direita, :math:`y` para cima e :math:`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 :math:`\theta`, o vetor :math:`z` e a origem não se alteram, o vetor unitário em :math:`x` é mapeado para :math:`(cos(\theta), sin(\theta), 0, 0)^T` e o vetor unitário em :math:`y` é mapeado para :math:`(-sin(\theta), cos(\theta), 0, 0)^T`. A matriz de rotação nesse caso é dada por: :math:`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: :math:`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}`, :math:`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}`. 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 :math:`z`. A face do cubo que está no plano de coordenadas :math:`xy` não se move. A face que se encontra no plano :math:`z = 1` é transladada por um vetor :math:`(h_x,h_y)`. Em geral, um ponto :math:`P = (P_x,P_y,P_z,1)` é transladado pelo vetor :math:`P_z(h_x,h_y,0,0)`. Esse vetor é ortogonal ao eixo :math:`z` e seu comprimento é proporcional à coordenada :math:`z` de :math:`P`. Isso é chamado de cisalhamento :math:`xy`. (Os cisalhamentos :math:`yz` e :math:`xz` são definidos de forma análoga.) Sob o cisalhamento :math:`xy`, a origem e os vetores unitários :math:`x` e :math:`y` permanecem inalterados. O vetor unitário :math:`z` é mapeado para :math:`(h_x , h_y , 1, 0)^T`. Assim, a matriz para esta transformação é: :math:`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: :math:`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}`, :math:`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}`. 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. 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 <https://jsitor.com/At5VwJBoQ>`__. 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 :math:`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 :math:`\times` H. Esboce, em pseudo-linguagem, como desenhar a figura abaixo: .. figure:: ./figuras/a11/brazil.png :alt: exercício 2 :name: fig-brazil :width: 60.0% :align: center Como desenhar essa figura usando a função ``desenheQuadrado()``? Para saber mais --------------- * Aula 8 das `notas de aula do Prof. Dave Mount <https://www.cs.umd.edu/~mount/427>`__. * Capítulo 5 do livro "Interactive Computer Graphics, 8th edition" de Angel e Shreiner.