O material desta seção é coberto em parte na Seção 21.1.3 do livro de Foley et al. A maior parte do material é baseado no livro “Advanced Animation and Rendering Techniques” de A. Watt e M. Watt (1992).
Até agora quando falamos de rotação em 3D utilizamos a representação de ângulos de Euler, decompondo a rotação nos 3 eixos \(x\), \(y\) e \(z\). Note que o resultado de uma rotação nesse caso depende da posição dos eixos e da ordem com cada eixo foi rotulado. Nessa aula veremos uma forma de representar rotações de forma invariante a mudanças rígidas do sistema de coordenadas.
Talvez uma forma mais natural de expressar rotações 3D ao redor da origem seja na forma de um eixo de rotação \(\vec{u}\) e um ângulo \(\theta\). Vamos considerar que \(\vec{u}\) tenha comprimento unitário.
Vamos ver como rodar um vetor \(\vec{v}\). Nosso objetivo é descrever a imagem de \(\vec{v}\) sob essa rotação em função de \(\theta\) e \(\vec{u}\). Seja \(R(\vec{v})\) essa imagem. Para derivá-la, vamos inicialmente decompor \(\vec{v}\) como a soma de suas componentes paralela e ortogonal a \(\vec{u}\), respectivamente:
\(\vec{v}_{\|} = (\vec{u} \cdot \vec{v}) \vec{u} \;\;\; \vec{v}_{\bot} = \vec{v} - \vec{v}_{\|} = \vec{v} - (\vec{u} \cdot \vec{v}) \vec{u}\)
Note que \(\vec{v}_{\|}\) não é afetada pela rotação, somente \(\vec{v}_{\bot}\) é rotacionado para uma nova posição \(R(\vec{v}_{\bot})\). Para determinar esta posição rotacionada, nós vamos construir um vetor que é ortogonal a \(\vec{v}_{\bot}\) contido no plano de rotação.
\(\vec{w} = \vec{u} \times \vec{v}_{\bot} = \vec{u} \times (\vec{v} - \vec{v}_{\|}) = (\vec{u} \times \vec{v})-(\vec{u}\times\vec{v}_{\|}) = \vec{u} \times \vec{v}\).
Fig. 26.1 Deslocamento angular. Fonte: Notas de aula do Prof. Dave Mount.¶
Por fim, como \(\vec{u}\) e \(\vec{v}_{\|}\) são paralelos, seu produto vetorial é zero. Claramente \(\vec{w}\) é ortogonal tanto ao \(\vec{v}_{\bot}\) quanto ao \(\vec{u}\). Além disso, como \(\vec{v}_{\bot}\) é ortogonal ao vetor unitário \(\vec{u}\), temos que \(\vec{w}\) tem o mesmo comprimento que \(\vec{v}_{\bot}\).
Agora considere o plano definido por \(\vec{v}_{\bot}\) e \(\vec{w}\). Temos que:
\(R(\vec{v}_{\bot}) = (\cos \theta) \vec{v}_{\bot} + (\sin \theta) \vec{w}\).
Portanto temos que
\(R(\vec{v}) = R(\vec{v}_{\|}) + R(\vec{v}_{\bot})\)
\(\;\;\; = R(\vec{v}_{\|}) + (\cos \theta) \vec{v}_{\bot} + (\sin \theta) \vec{w}\)
\(\;\;\; = (\vec{u}\cdot \vec{v}) \vec{u} + (\cos \theta) (\vec{v} - (\vec{u}\cdot \vec{v}) \vec{u} ) + (\sin \theta) \vec{w}\)
\(\;\;\; = (\cos \theta) \vec{v} + (1-\cos \theta) \vec{u} (\vec{u}\cdot \vec{v}) + (\sin \theta) (\vec{u} \times \vec{v})\).
Essa última expressão é a imagem de \(\vec{v}\) sob a rotação \((\theta, \vec{u})\). Note que, diferente dos ângulos de Euler, essa rotação é expressa inteiramente na forma de quantidades geométricas, que não dependem da escolha de coordenadas. Apesar de ser uma vantagem sobre os ângulos de Euler, essa forma é um tanto difícil de manipular.
Vamos agora tratar de um assunto aparentemente não relacionado ao nosso tema. Mas procure lembrar-se da última expressão, pois ela vai reaparecer de forma surpreendente.
Essa estória começa no início do século 19, quanto o grande matemático Hamilton estava a procura de uma generalização do sistema de números complexos. Números imaginários podem ser considerados como uma combinação linear de elementos de suas bases: 1 e \(i\), que satisfazem a regra de multiplicação \(1^2 = 1\), \(i^2 = -1\) e \(1*i = i*1 = i\). A interpretação de \(i=\sqrt{-1}\) surge da segunda regra.
Hamilton procurou por uma generalização envolvendo dois valores de base imaginários, \(i\) e \(j\), mas não conseguiu fazer funcionar. Após alguns anos ele inventou o seguinte truque, considerar 3 valores imaginários \(i\), \(j\) e \(k\), que se comportam da seguinte forma:
\(i^2 = j^2 = k^2 = -1 \;\;\; ij = k, jk = i, ki = j\)
Combinando essas regras, temos que \(ji=-k, kj=-i\) e \(ik=-j\). Um quaternion é definido com a generalização de um número complexo na forma
\(\mathbf{q} = q_0 + q_1 i + q_2 j + q_3 k\).
Veremos que um quaternion possui uma semelhança grande com a expressão de deslocamento angular que vimos anteriormente. Em particular, podemos reescrever a notação de quaternion em termos de um escalar e um vetor na forma
\(\mathbf{q} = (s, \vec{u}) = s + u_x i + u_y j + u_z k\).
Aplicando as regras de multiplicação anteriores, podemos derivar a seguinte regra de multiplicação de quaternions (que você mesmo pode tentar desenvolver como exercício):
\(\mathbf{q}_1\mathbf{q}_2 = (s_1 s_2 - (\vec{u}_1\cdot\vec{u}_2), s_1\vec{u}_2 + s_2\vec{u}_1 + \vec{u}_1 \times \vec{u}_2)\).
A multiplicação de quaternions é associativa mas não comutativa.
O conjugado de um quaternion \(\mathbf{q} = (s, \vec{u})\) é definido como:
\(\bar{\mathbf{q}} = (s, -\vec{u})\).
É simples demonstrar que o produto de um quaternion e seu conjugado tem um componente vetorial nulo e portanto pode ser considerado como um escalar.
A magnitude de um quaternion é dada pela raiz quadrada do produto
\(|\mathbf{q}|^2 = \mathbf{q} \bar{\mathbf{q}} = s^2 + |\vec{u}|^2\).
Note que estamos abusando um pouco da notação. Um quaternion unitário tem magnitude 1, ou seja, \(|\mathbf{q}|=1\). Um quaternion puro tem componente escalar nulo
\(\mathbf{p} = (0, \vec{v})\).
Qualquer quaternion de magnitude não nula tem um inverso multiplicativo, dado por
\(\mathbf{q}^{-1} = \cfrac{1}{|\mathbf{q}|^2} \bar{\mathbf{q}}\).
Tente multiplicar \(\mathbf{q} \mathbf{q}^{-1}\) para verificar a razão. Observe que se \(\mathbf{q}\) for um quanternion unitário, então \(\mathbf{q}^{-1} = \bar{\mathbf{q}}\).
Finalmente veremos o que quaternions tem a ver com rotação!
Nós vamos representar uma rotação usando um quaternion unitário \(\mathbf{q}\) (veremos por que mais tarde). Dado um ponto qualquer \(P = (v_x, v_y, v_z)\) no espaço 3D, vamos representa-lo com um quaternion puro \(\mathbf{p}=(0, \vec{v})\). A imagem de \(\mathbf{p}\) sob uma rotação \(\mathbf{q}\) também será um quaternion puro (e por isso é fácil mapeá-lo de volta a um ponto 3D).
Vamos definir o operador de rotação como
\(R_{\mathbf{q}}(\mathbf{p}) = \mathbf{q}\mathbf{p}\mathbf{q}^{-1}\).
Ao aplicarmos a regra de multiplicação, e usando o fato de que \(\mathbf{q}^{-1} = \bar{\mathbf{q}}\) para quaternions unitários, é possível derivar que:
\(R_{\mathbf{q}}(\mathbf{p}) = (0, (s^2 - (\vec{u}\cdot\vec{u}))\vec{v} + 2\vec{u}(\vec{u}\cdot\vec{v}) + 2s(\vec{u}\times\vec{v}))\).
Novamente, deixamos essa derivação como exercício.
Hmmm… mas o que isso tem a ver com rotação? Vamos tentar mostrar essa expressão de uma forma mais sugestiva. Como \(\mathbf{q}\) tem magnitude unitária, podemos expressa-la como
\(\mathbf{q} = (\cos \theta, (\sin \theta) \vec{u}),\;\;\;\; \text{onde} \;\; |\vec{u}| = 1\).
Substituindo essa expressão na anterior temos:
\(R_{\mathbf{q}}(\mathbf{p}) = (0, (\cos^2 \theta - \sin^2 \theta)\vec{v} + 2(\sin^2 \theta)\vec{u}(\vec{u}\cdot\vec{v})+2\cos\theta \sin\theta (\vec{u}\times\vec{v}))\)
\(\;\;\; = (0, (\cos 2\theta)\vec{v} + (1-\cos 2\theta)\vec{u}(\vec{u}\cdot\vec{v}) + \sin 2\theta(\vec{u} \times \vec{v}))\).
Reveja agora a expressão que derivamos para o caso de deslocamento angular. Note que a parte vetorial desse quaternion é idêntica, exceto que temos \(2\theta\) no lugar de \(\theta\).
De forma resumida devemos representar pontos 3D como quaternions puros
\(\mathbf{p} = (0, \vec{v})\),
e representar uma rotação de um ângulo \(\theta\) ao redor de um vetor unitário \(\vec{u}\) como um quaternion unitário
\(\mathbf{q}=(\cos(\theta/2), \sin(\theta/2)\vec{u})\).
Dessa forma, a imagem do ponto rotacionado será dado pela parte vetorial do resultado do operador de rotação de quaternions \(R_{\mathbf{q}}(\mathbf{p})\).
Considere a rotação 3D mostrada na figura abaixo,que pode ser realizada por uma rotação de -90 graus ao redor do eixo y. Assim, \(\theta = -\pi/2\), and \(\vec{u}=(0,1,0)\). Assim o quaternion que representa esta rotação é:
\(\mathbf{q} = (\cos(-\pi/4), \sin(-\pi/4)(0,1,0)) = (1 / \sqrt{2}, (0, -1/\sqrt{2}, 0))\).
Fig. 26.2 Exemplo de rotação usando quaternion. Fonte: Notas de aula do Prof. Dave Mount.¶
Considere o ponto \(P\) com coordenada homogênea (1,0,0,1), no canto extremo do vetor unitário \(x\), que pode ser representado pelo vetor \(\vec{v} = (1,0,0)\) e pelo quaternion puro \(\mathbf{p} = (0, \vec{v}) = (0, (1,0,0))\).
Podemos então aplicar o operador de rotação:
\(R_{\mathbf{q}}(\mathbf{p}) = (0, (1/2 - 1/2)(1,0,0) + 2(0,1,0)0 + (2/\sqrt{2})((0,-1/\sqrt{2},0)\times(1,0,0)))\)
\(\;\;\; = (0, (0,0,0) + (0,0,0) + (-1)(0,0,-1))\)
\(\;\;\; = (0, (0,0,1))\).
Mostramos que cada quaternion unitário corresponde a uma rotação 3D. Essa é uma representação elegante mas será que podemos manipular rotações usando operações com quaternions?
A resposta é sim! Em particular, o resultado da multiplicação de dois quaternions unitários também é um quaternion unitário. Além disso, o resultado do produto corresponde a composição de duas rotações. Em particular, dados dois quaternions unitários \(\mathbf{q}\) e \(\mathbf{q}'\), a rotação por \(\mathbf{q}\) seguida por outra rotação \(\mathbf{q}'\), é equivalente à rotação \(\mathbf{q}'' = \mathbf{q}' \mathbf{q}\). Ou seja:
\(R_{\mathbf{q}'} R_{\mathbf{q}} = R_{\mathbf{q}''} \;\;\;\; \text{onde} \;\; \mathbf{q}'' = \mathbf{q}' \mathbf{q}\).
Essa regra pode ser derivada pela propriedade associativa da multiplicação de quaternions, e o fato que \((\mathbf{q}\mathbf{q}')^{-1} = \mathbf{q}^{-1} \mathbf{q}'^{-1}\), como mostrado a seguir:
\(R_{\mathbf{q}'}(R_{\mathbf{q}}(\mathbf{p})) = \mathbf{q}' (\mathbf{q}\mathbf{p}\mathbf{q}^{-1}) \mathbf{q}'^{-1}\)
\(\;\;\;\;\;\; = (\mathbf{q}' \mathbf{q}) \mathbf{p} (\mathbf{q}^{-1} \mathbf{q}'^{-1})\)
\(\;\;\;\;\;\; = (\mathbf{q}' \mathbf{q}) \mathbf{p} (\mathbf{q} \mathbf{q}')^{-1}\)
\(\;\;\;\;\;\; = \mathbf{q}'' \mathbf{p} \mathbf{q}''^{-1}\)
\(R_{\mathbf{q}''}(\mathbf{p})\).
Quaternions constituem uma forma muito elegante de representar rotações em 3D. Um problema importante que pode ser resolvido usando quaternions é o de interpolar duas orientações de forma suave.
Já sabemos como representar duas orientações de um objeto, digamos uma orientação inicial e outra final, no espaço 3D usando dois quaternions \(\mathbf{q}\) e \(\mathbf{p}\). Então, para interpolar suavemente entre essas duas orientações (para criar, por exemplo uma animação), basta calcular outros quaternions entre \(\mathbf{q}\) e \(\mathbf{p}\). Não se trata realmente de uma interpolação linear pois os quaternions precisam manter seu comprimento unitário. Seria mais como interpolar dois pontos ao longo da superfície de uma esfera.
No entanto, uma vez que tenhamos uma representação por quaternions, precisamos de uma forma para passar a transformação desejada para a API gráfica (como o OpenGL). Em particular, dado um quaternion unitário
\(\mathbf{q} = (\cos(\theta/2), \sin(\theta/2) \vec{u}) = (w, (x, y, z))\),
qual seria a transformação afim correspondente expressa na forma de uma matriz de rotação?
Podemos expandir a definição do operador de rotação \(R_{\mathbf{q}}(\mathbf{p})\) e não é muito difícil chegar na seguinte matriz (homogênea) equivalente:
\(\begin{pmatrix}1-2y^2-2z^2 & 2xy - 2wz & 2xz + 2wy & 0\\ 2xy + 2wz & 1-2x^2 - 2z^2 & 2yz - 2wx & 0\\ 2xz - 2wy & 2yz + 2wx & 1-2x^2 - 2y^2 & 0\\ 0 & 0 & 0 & 1\end{pmatrix}\)
Assim, dado um quaternion interpolante, você pode gerar essa matriz e utiliza-la para a renderização de suas animações.