Archive

Archive for August, 2009

Transformaciones inversas, inversión de matrices

August 17th, 2009 No comments

En programación 3D en tiempo real es imprescindible optimizar todo lo posible, y la inversión de matrices homogéneas es uno de los casos complejos (aparentemente) en donde se puede conseguir una excelente optimización siguiendo las reglas del álgebra matricial y conociendo cómo ha sido generada la matriz a invertir (conociendo su estructura).

Nota: En este post usaré el formato fila (pre-multiplicación) para representar las matrices y la formulación.

Vamos a ver primero la inversión aislada de los tres casos básicos, matriz de rotación, escala y traslación:

Si la matriz de rotación R es ortogonal (si sus tres ejes son perpendiculares entre si), entonces su inversa es simplemente su traspuesta:

R=\begin{bmatrix}RXx&RXy&RXz&0\\RYx&RYy&RYz&0\\RZx&RZy&RZz&0\\0&0&0&1\end{bmatrix}

R^{-1}=R^{T}=\begin{bmatrix}RXx&RYx&RZx&0\\RXy&RYy&RZy&0\\RXz&RYz&RZz&0\\0&0&0&1\end{bmatrix}

La inversa de una matriz de escala S es muy sencilla:

S=\begin{bmatrix}Sx&0&0&0\\0&Sy&0&0\\0&0&Sz&0\\0&0&0&1\end{bmatrix}

S^{-1}=\begin{bmatrix}\frac{1}{Sx}&0&0&0\\0&\frac{1}{Sy}&0&0\\0&0&\frac{1}{Sz}&0\\0&0&0&1\end{bmatrix}

Y la inversa de una matriz de traslación T también es muy simple:

T=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\Tx&Ty&Tz&1\end{bmatrix}

T^{-1}=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\-Tx&-Ty&-Tz&1\end{bmatrix}

Ahora veamos un caso más práctico, cómo invertir una matriz de transformación RT (es decir, una matriz que primero realiza una rotación y luego una traslación). Es imprescindible saber siempre el orden de las transformaciones contenidas en una matriz para poder optimizar el cálculo de su inversa ya que vamos a optimizar los cálculos por partes y siguiendo una regla muy simple; la inversa del producto de dos matrices es igual al producto invertido de las inversas de dichas matrices: (A*B)^{-1}=B^{-1}*A^{-1}

Con esta regla tan simple podemos invertir una matriz RT multiplicando la inversa de la traslación por la inversa de la rotación: (R*T)^{-1}=T^{-1}*R^{-1} y como ya sabemos que la inversa de una matriz ortogonal es su traspuesta la fórmula final será: (R*T)^{-1}=T^{-1}*R^{T}

Con lápiz y papel calculamos la estructura final de la matriz inversa de una transformación RT:

\large(R*T)^{-1}=\begin{bmatrix}RXx&RYx&RZx&0\\RXy&RYy&RZy&0\\RXz&RYz&RZz&0\\-(RXx*Tx+RXy*Ty+RXz*Tz)&-(RYx*Tx+RYy*Ty+RYz*Tz)&-(RZx*Tx+RZy*Ty+RZz*Tz)&1\end{bmatrix}

Para invertir una matriz TR es incluso más simple: (T*R)^{-1}=R^{T}*T^{-1}

(T*R)^{-1}=\begin{bmatrix}RXx&RYx&RZx&0\\RXy&RYy&RZy&0\\RXz&RYz&RZz&0\\-Tx&-Ty&-Tz&1\end{bmatrix}

Ahora veremos cómo invertir matrices que incluyen transformaciones de escala isométrica (que significa que se ha aplicado la misma escala a los tres ejes), empecemos por la clásica transformación SRT (donde primero se escala, luego se rota y finalmente se traslada). Como siempre su inversa será: (S*R*T)^{-1}=T^{-1}*R^{T}*S^{-1}

Volvemos a coger el lápiz y calculamos:

\large(S*R*T)^{-1}=\begin{bmatrix}\frac{RXx}{S}&\frac{RYx}{S}&\frac{RZx}{S}&0\\\frac{RXy}{S}&\frac{RYy}{S}&\frac{RZy}{S}&0\\\frac{RXz}{S}&\frac{RYz}{S}&\frac{RZz}{S}&0\\-\frac{RXx*Tx+RXy*Ty+RXz*Tz}{S}&-\frac{RYx*Tx+RYy*Ty+RYz*Tz}{S}&-\frac{RZx*Tx+RZy*Ty+RZz*Tz}{S}&1\end{bmatrix}

La misma inversa SRT pero de una matriz con escalado anisométrico (escalado no idéntico en los tres ejes) sería:

\large(S*R*T)^{-1}=\begin{bmatrix}\frac{RXx}{Sx}&\frac{RYx}{Sy}&\frac{RZx}{Sz}&0\\\frac{RXy}{Sx}&\frac{RYy}{Sy}&\frac{RZy}{Sz}&0\\\frac{RXz}{Sx}&\frac{RYz}{Sy}&\frac{RZz}{Sz}&0\\-\frac{RXx*Tx+RXy*Ty+RXz*Tz}{Sx}&-\frac{RYx*Tx+RYy*Ty+RYz*Tz}{Sy}&-\frac{RZx*Tx+RZy*Ty+RZz*Tz}{Sz}&1\end{bmatrix}

Para terminar veremos la inversa de una matriz que represente una transformación RTS. La inversa con escalado isométrico es:

\large(R*T*S)^{-1}=\begin{bmatrix}\frac{RXx}{S}&\frac{RYx}{S}&\frac{RZx}{S}&0\\\frac{RXy}{S}&\frac{RYy}{S}&\frac{RZy}{S}&0\\\frac{RXz}{S}&\frac{RYz}{S}&\frac{RZz}{S}&0\\-(RXx*Tx+RXy*Ty+RXz*Tz)&-(RYx*Tx+RYy*Ty+RYz*Tz)&-(RZx*Tx+RZy*Ty+RZz*Tz)&1\end{bmatrix}

Y la inversa de una matriz RTS con escala anisométrica es:

\large(R*T*S)^{-1}=\begin{bmatrix}\frac{RXx}{Sx}&\frac{RYx}{Sy}&\frac{RZx}{Sz}&0\\\frac{RXy}{Sx}&\frac{RYy}{Sy}&\frac{RZy}{Sz}&0\\\frac{RXz}{Sx}&\frac{RYz}{Sy}&\frac{RZz}{Sz}&0\\-(RXx*Tx+RXy*Ty+RXz*Tz)&-(RYx*Tx+RYy*Ty+RYz*Tz)&-(RZx*Tx+RZy*Ty+RZz*Tz)&1\end{bmatrix}

Si teneis dificultades para ver alguna de las matrices pulsad con el botón derecho del ratón sobre ella y elegid Abrir/Ver imagen.

Formato interno de las matrices en OpenGL

August 15th, 2009 No comments

Desde los inicios de OpenGL existe una confusión bastante importante sobre cómo administra internamente las matrices. Unos piensan que las almacena en formato fila y otros piensan que lo hace en formato columna. Pues lo cierto es que ni una cosa ni la otra!
OpenGL almacena las matrices en un array unidimensional de 16 elementos y es el programador quien decide cómo prefiere interpretarlas.

Quedará más claro con un ejemplo:

1
2
GLfloat m[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m);


Con el código anterior leemos la matriz actual MODELVIEW y la almacenamos en el array m.

¿Y cómo están estructurados los elementos de la matriz? Pues muy sencillo, secuencialmente:

m = \{m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15\}

Ahora el programador puede decidir interpretarlo como una matriz de tipo columna:

\begin{bmatrix}m0&m4&m8&m12\\m1&m5&m9&m13\\m2&m6&m10&m14\\m3&m7&m11&m15\end{bmatrix}

O como una matriz de tipo fila:

\begin{bmatrix}m0&m1&m2&m3\\m4&m5&m6&m7\\m8&m9&m10&m11\\m12&m13&m14&m15\end{bmatrix}

Matemáticamente es indiferente cómo prefieras interpretarlas, pero recuerda que si utilizas el formato columna debes realizar post-multiplicaciones, y si usas el formato fila pre-multiplicaciones.

Supongamos que tienes una matriz R que representa una rotación y la matriz T que realiza una traslación, y quieres combinarlas en una matriz C, de forma que la matriz de trasformación resultante realice primero la rotación y luego la traslación. Si usas el formato fila tendrás que hacer una pre-multiplicación: C=R*T (Esto se lee: la matriz R pre-multiplica a la matriz T).
Pero si usas el formato columna el orden se invierte ya que es una post-multiplicación: C=T*R (Y esto se lee: la matriz R post-multiplica a la matriz T).

Otro ejemplo; quieres transformar el vector V=\{x,y,z,0\} por la matriz M para obtener el vector transformado V'.
Si la matriz M es de tipo fila pre-multiplicas: V'=V*M
En cambio, si es de tipo columna post-multiplicas: V'=M*V

En algebra matricial siempre se multiplican filas por columnas, por lo tanto si la matriz M es de tipo fila V y V' deben ser interpretados como un vectores fila, y si M es de tipo columna entonces V y V' deberán ser interpretados como vectores columna.

Un último ejemplo; transforma el vector V por las matrices A,B,C (en éste orden).
La pre-multiplicación sigue el orden natural de las transformaciones por lo que resulta más intuitiva: V'=V*A*B*C
La post-multiplicación sería el proceso inverso: V'=C*B*A*V

Para terminar un truco para no liarte; cuando utilices matrices y vectores columna (post-multiplicación) debes interpretar las transformaciones como series de funciones: V'=C(B(A(V)))