Matrices
2.0 ¿Que es Matrix?
¿Te gustaria saber lo que es Matrix?. Matrix nos rodea. Esta por todas partes, incluso ahora, en esta misma web,
puedes verla si miras por la ventana o al encender el ordenador. Puedes sentirla, cuando vas a cine, cuando programas,
cuando juegas a tu juegos. Es la relidad que a sido colocada ante ti para ocultarte que vives una mentira: todo lo que ves no es
3D, y es gracias a Matrix.
¿Por que usar matrices? Gracias a ellas podremos trasladar, escalar y rotar en una misma operacion. Asi, si debemos aplicar a una geometria
compleja una serie de transformaciones, no tendremos que calcularlas para cada vertice de esta, solo tendremos que hacerlo una vez, y aplicar el resultador a la
geometria. Esto, ademas de simplificar mucho el calculo, es muy interesante a la hora de optimizar. Para hacer esas tres operaciones en una, bastara con usar la multiplicacion de matrices. Si
quieres optimizar tus calculos, solo tendras que preocuparte de hacer una multiplacion rapida. Ademas, la mayoria de las tarjetas actuales realizan los calculos sobre
matrices por hardware, liberando a la CPU de esa costosa tarea.
La manera mas simple de describir que es una matriz seria una tabla de elementos ordenadas en filas y columnas. Si una matriz A tiene n filas y
m columnas, diremos que A es una matriz de mxn:
![]()
En este caso, tenemos una matriz de 3x3. Para referirnos a un elemento de una matriz, indicaremos la fila y la columna del elemento, asi, M10 valdria 8.
Referirnos a un elemento cualquiera de la matriz seria Mij, donde i representa las filas y j las columnas.
Esta forma de identificar elementos se conoce como Row Major, y es el sistema que usa DirectX (lo contrario seria Column Major).
Otra cosa a tener encuenta, seria si empezar en 0 (zero-based) los indices o no. Nuestras matrices seran zero-based:

Las que nos interesan a nosotros son principalmente las de 4x4 (aunque tambien tendremos de 3x3), puedes ver la implementacion completa en
matrix.h y en matrix.cpp. Las definiremos asi:
3×3
union
{
struct
{
real m00, m01, m02,
m10, m11, m12,
m20, m21, m22;
};
struct
{
real row0[3], row1[3], row2[3];
};
real ij[3][3];
real e[9];
};
|
4×4
union
{
struct
{
real m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33;
};
struct
{
real row0[4], row1[4], row2[4], row3[4];
};
real ij[4][4];
real e[16];
};
|
2.1 Matrices especiales
Si el numero de filas es igual al numero de columnas diremos que es una matriz cuadrada.
Si todos sus elementos son cero, diremos que es una matriz nula.
A la serie de elementos de una matriz que cumplen i = j se le llama diagonal de la matriz. Una matriz no nula, con todos los elementos de su diagonal a
cero, es una matriz diagonal.
Si cojemos una matriz nula y cambiamos su diagonal por unos, tendremos la llamada matriz identidad, llamada I:
![]()
2.2 Operaciones basicas
Las operaciones suma, resta y multiplicacion por escalar de dos matrices n x m, son muy sencillas y las podeis ver en el codigo fuente:
matrix.h y matrix.cpp.
La suma de dos matrices, consiste en sumar el elemento de la primera con el de la segunda: Cij = Aij + Bij
![]()
La forma de restar es similar: Cij = Aij - Bij. Multiplicar por un escalar una matriz, es tambien muy sencillo: Cij = aAij. Para cualquier matriz, A, se cumple que: 0A = 0.
La multiplicacion de matrices se usa constantemente y es muy importante. Para multiplicar 2 matrices, A y B, el numero de
columnas de A debe ser igual al numero de filas de B. Asi, si A es una matriz de n x m y B es de
m x p, el resultado de hacer AB sera una matriz de n x p, Cnp = Anm x Bmp.
Basicamente consiste en multiplicar los elementos de las columnas de la primera, por los elementos de las filas de la segunda:
![]()
![]()
La letra al final de la formula (sigma) significa sumatorio, y se podria entender como “la suma de todos los AikBkj para k = 1 hasta n“, osea, como el FOR de C.
mat4 mat4::operator * (const mat4& m) const
{
mat4 prod;
for (int row = 0; row < 4; row++)
for (int col = 0; col < 4; col++)
prod[row][col] = ij[row][0]*m[0][col] + ij[row][1]*m[1][col] + ij[row][2]*m[2][col] + ij[row][3]*m[3][col];
return prod;
}
|
Esta forma de multiplicar es facil de entender, pero no es la mas optima. La multiplicacion de matrices la usaremos constantemente en
nuestros calculos, y seria mas rapido si desenrrollamos (quitar los bucles, unroll en ingles). Quedaria asi:
mat4 mat4::operator * (const mat4& m) const
{
mat4 prod;
prod[0][0] = ij[0][0]*m[0][0] + ij[0][1]*m[1][0] + ij[0][2]*m[2][0] + ij[0][3]*m[3][0];
prod[0][1] = ij[0][0]*m[0][1] + ij[0][1]*m[1][1] + ij[0][2]*m[2][1] + ij[0][3]*m[3][1];
prod[0][2] = ij[0][0]*m[0][2] + ij[0][1]*m[1][2] + ij[0][2]*m[2][2] + ij[0][3]*m[3][2];
prod[0][3] = ij[0][0]*m[0][3] + ij[0][1]*m[1][3] + ij[0][2]*m[2][3] + ij[0][3]*m[3][3];
prod[1][0] = ij[1][0]*m[0][0] + ij[1][1]*m[1][0] + ij[1][2]*m[2][0] + ij[1][3]*m[3][0];
prod[1][1] = ij[1][0]*m[0][1] + ij[1][1]*m[1][1] + ij[1][2]*m[2][1] + ij[1][3]*m[3][1];
prod[1][2] = ij[1][0]*m[0][2] + ij[1][1]*m[1][2] + ij[1][2]*m[2][2] + ij[1][3]*m[3][2];
prod[1][3] = ij[1][0]*m[0][3] + ij[1][1]*m[1][3] + ij[1][2]*m[2][3] + ij[1][3]*m[3][3];
prod[2][0] = ij[2][0]*m[0][0] + ij[2][1]*m[1][0] + ij[2][2]*m[2][0] + ij[2][3]*m[3][0];
prod[2][1] = ij[2][0]*m[0][1] + ij[2][1]*m[1][1] + ij[2][2]*m[2][1] + ij[2][3]*m[3][1];
prod[2][2] = ij[2][0]*m[0][2] + ij[2][1]*m[1][2] + ij[2][2]*m[2][2] + ij[2][3]*m[3][2];
prod[2][3] = ij[2][0]*m[0][3] + ij[2][1]*m[1][3] + ij[2][2]*m[2][3] + ij[2][3]*m[3][3];
prod[3][0] = ij[3][0]*m[0][0] + ij[3][1]*m[1][0] + ij[3][2]*m[2][0] + ij[3][3]*m[3][0];
prod[3][1] = ij[3][0]*m[0][1] + ij[3][1]*m[1][1] + ij[3][2]*m[2][1] + ij[3][3]*m[3][1];
prod[3][2] = ij[3][0]*m[0][2] + ij[3][1]*m[1][2] + ij[3][2]*m[2][2] + ij[3][3]*m[3][2];
prod[3][3] = ij[3][0]*m[0][3] + ij[3][1]*m[1][3] + ij[3][2]*m[2][3] + ij[3][3]*m[3][3];
return prod;
}
|
La multiplicion de matrices, no es conmutativa. Por lo general, AB no es igual que BA. Una excepcion seria el caso de multiplicar una matriz, por la matriz identidad, resultando la misma matriz original, AI = IA = A.
Multiplicar una matriz por la matriz nula, resulta la matriz nula.
Si a una matriz le cambiamos los elementos Aij por Aji (cambiar filas por columnas), habremos obtenido la traspuesta de A, que notaremos como AT. Como resulta logico
imaginar, se cumple que (AT)T = A.
![]()
| Teorema 1[2.2] |
|
Dados dos escalares, a y b, y tres matrices de nxm A, B y C, se cumple lo siguiente:
(a) A + B = B + A (b) (A + B) + C = A + (B + C) (c) a(bA) = (ab)A (d) a(A + B) = aA + aB (e) (a + b)A = aA + bB (f) (AB)T = ATBT |
Una matriz M de n x n (cuadrada) diremos que tiene inversa, llamada M-1, si MM-1 = M-1M = I.
No todas las matrices tienen inversa. A aquellas matrices que no tienen inversa se las llama singulares. Un ejemplo de matriz singular seria aquella que
tubiera una fila y/o columna llena de ceros. Una matriz M es invertible (que tiene inversa) si y solo si MT es invertible.
Llamaremos matriz ortogonal a aquellas que su inversa es igual a su traspuesta, osea, que una matriz es ortogonal, si y solo si M-1 = MT.
Una propiedad interesante de estas matrices, es que mantienen el modulo y el angulo de un vector cuando son usadas para trasformaciones.
Existen otras muchas operaciones, como el determinante, vectores eigen, diagonalizar, etc que no veremos por no ser muy utiles por ahora.
2.3 Transformaciones
El conjunto de operaciones que vamos a necesitar hacer sobre vectores 3D podemos llamarlas transformaciones. Escalar un vector seria un
ejemplo de transformacion lineal, osea que el resultado es proporcional a los datos iniciales. Trasladar vectores no es una transformacion lineal, pero haciendo unos cambios podremos
hacerlo con matrices (que solo sirven para transformaciones lineales).
Esta es la razon de usar vectores y matrices de 4 dimensiones y no de 3. A esta 4 coordenada en nuestros vectores la
llamaremos coordenada homogenea (comunmente designada como w, que normalmente podremos olvidar y que suele valer 1) y al proceso de dividir las 3 coordenadas (x, y y z) por w, lo llamaremos
homogeneizar. Podriamos pensar que este proceso seria como proyectar un vector de dimension N en N-1
Para realizar estar operaciones, nos apoyaremos en las matrices, ya que, cualquier transformacion lineal de Rn a Rm puede ser representada por una matriz de m x n.
Para transformar un vector, que puede ser representado como una matriz de nx1, calculamos Lv, donde L es la matriz de transformacion linear (llamada matriz de induccion) y v es el vector. El
resultado seria un vector en Rm
Las tipicas operaciones de un motor 3D sobre vectores pueden realizarse con matrices de 3×3, menos la traslacion que requiere una de 4×4. Es conveniente usar el mismo tamaño para todas,
asi que usaremos matrices de 4×4. Esto significa q2.0 ¿Que es Matrix?
¿Te gustaria saber lo que es Matrix?. Matrix nos rodea. Esta por todas partes, incluso ahora, en esta misma web,
puedes verla si miras por la ventana o al encender el ordenador. Puedes sentirla, cuando vas a cine, cuando programas,
cuando juegas a tu juegos. Es la relidad que a sido colocada ante ti para ocultarte que vives una mentira: todo lo que ves no es
3D, y es gracias a Matrix.
¿Por que usar matrices? Gracias a ellas podremos trasladar, escalar y rotar en una misma operacion. Asi, si debemos aplicar a una geometria
compleja una serie de transformaciones, no tendremos que calcularlas para cada vertice de esta, solo tendremos que hacerlo una vez, y aplicar el resultador a la
geometria. Esto, ademas de simplificar mucho el calculo, es muy interesante a la hora de optimizar. Para hacer esas tres operaciones en una, bastara con usar la multiplicacion de matrices. Si
quieres optimizar tus calculos, solo tendras que preocuparte de hacer una multiplacion rapida. Ademas, la mayoria de las tarjetas actuales realizan los calculos sobre
matrices por hardware, liberando a la CPU de esa costosa tarea.
La manera mas simple de describir que es una matriz seria una tabla de elementos ordenadas en filas y columnas. Si una matriz A tiene n filas y
m columnas, diremos que A es una matriz de mxn:
![]()
En este caso, tenemos una matriz de 3x3. Para referirnos a un elemento de una matriz, indicaremos la fila y la columna del elemento, asi, M10 valdria 8.
Referirnos a un elemento cualquiera de la matriz seria Mij, donde i representa las filas y j las columnas.
Esta forma de identificar elementos se conoce como Row Major, y es el sistema que usa DirectX (lo contrario seria Column Major).
Otra cosa a tener encuenta, seria si empezar en 0 (zero-based) los indices o no. Nuestras matrices seran zero-based:

Las que nos interesan a nosotros son principalmente las de 4x4 (aunque tambien tendremos de 3x3), puedes ver la implementacion completa en
matrix.h y en matrix.cpp. Las definiremos asi:
3×3
union
{
struct
{
real m00, m01, m02,
m10, m11, m12,
m20, m21, m22;
};
struct
{
real row0[3], row1[3], row2[3];
};
real ij[3][3];
real e[9];
};
|
4×4
union
{
struct
{
real m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33;
};
struct
{
real row0[4], row1[4], row2[4], row3[4];
};
real ij[4][4];
real e[16];
};
|
2.1 Matrices especiales
Si el numero de filas es igual al numero de columnas diremos que es una matriz cuadrada.
Si todos sus elementos son cero, diremos que es una matriz nula.
A la serie de elementos de una matriz que cumplen i = j se le llama diagonal de la matriz. Una matriz no nula, con todos los elementos de su diagonal a
cero, es una matriz diagonal.
Si cojemos una matriz nula y cambiamos su diagonal por unos, tendremos la llamada matriz identidad, llamada I:
![]()
2.2 Operaciones basicas
Las operaciones suma, resta y multiplicacion por escalar de dos matrices n x m, son muy sencillas y las podeis ver en el codigo fuente:
matrix.h y matrix.cpp.
La suma de dos matrices, consiste en sumar el elemento de la primera con el de la segunda: Cij = Aij + Bij
![]()
La forma de restar es similar: Cij = Aij - Bij. Multiplicar por un escalar una matriz, es tambien muy sencillo: Cij = aAij. Para cualquier matriz, A, se cumple que: 0A = 0.
La multiplicacion de matrices se usa constantemente y es muy importante. Para multiplicar 2 matrices, A y B, el numero de
columnas de A debe ser igual al numero de filas de B. Asi, si A es una matriz de n x m y B es de
m x p, el resultado de hacer AB sera una matriz de n x p, Cnp = Anm x Bmp.
Basicamente consiste en multiplicar los elementos de las columnas de la primera, por los elementos de las filas de la segunda:
![]()
![]()
La letra al final de la formula (sigma) significa sumatorio, y se podria entender como “la suma de todos los AikBkj para k = 1 hasta n“, osea, como el FOR de C.
mat4 mat4::operator * (const mat4& m) const
{
mat4 prod;
for (int row = 0; row < 4; row++)
for (int col = 0; col < 4; col++)
prod[row][col] = ij[row][0]*m[0][col] + ij[row][1]*m[1][col] + ij[row][2]*m[2][col] + ij[row][3]*m[3][col];
return prod;
}
|
Esta forma de multiplicar es facil de entender, pero no es la mas optima. La multiplicacion de matrices la usaremos constantemente en
nuestros calculos, y seria mas rapido si desenrrollamos (quitar los bucles, unroll en ingles). Quedaria asi:
mat4 mat4::operator * (const mat4& m) const
{
mat4 prod;
prod[0][0] = ij[0][0]*m[0][0] + ij[0][1]*m[1][0] + ij[0][2]*m[2][0] + ij[0][3]*m[3][0];
prod[0][1] = ij[0][0]*m[0][1] + ij[0][1]*m[1][1] + ij[0][2]*m[2][1] + ij[0][3]*m[3][1];
prod[0][2] = ij[0][0]*m[0][2] + ij[0][1]*m[1][2] + ij[0][2]*m[2][2] + ij[0][3]*m[3][2];
prod[0][3] = ij[0][0]*m[0][3] + ij[0][1]*m[1][3] + ij[0][2]*m[2][3] + ij[0][3]*m[3][3];
prod[1][0] = ij[1][0]*m[0][0] + ij[1][1]*m[1][0] + ij[1][2]*m[2][0] + ij[1][3]*m[3][0];
prod[1][1] = ij[1][0]*m[0][1] + ij[1][1]*m[1][1] + ij[1][2]*m[2][1] + ij[1][3]*m[3][1];
prod[1][2] = ij[1][0]*m[0][2] + ij[1][1]*m[1][2] + ij[1][2]*m[2][2] + ij[1][3]*m[3][2];
prod[1][3] = ij[1][0]*m[0][3] + ij[1][1]*m[1][3] + ij[1][2]*m[2][3] + ij[1][3]*m[3][3];
prod[2][0] = ij[2][0]*m[0][0] + ij[2][1]*m[1][0] + ij[2][2]*m[2][0] + ij[2][3]*m[3][0];
prod[2][1] = ij[2][0]*m[0][1] + ij[2][1]*m[1][1] + ij[2][2]*m[2][1] + ij[2][3]*m[3][1];
prod[2][2] = ij[2][0]*m[0][2] + ij[2][1]*m[1][2] + ij[2][2]*m[2][2] + ij[2][3]*m[3][2];
prod[2][3] = ij[2][0]*m[0][3] + ij[2][1]*m[1][3] + ij[2][2]*m[2][3] + ij[2][3]*m[3][3];
prod[3][0] = ij[3][0]*m[0][0] + ij[3][1]*m[1][0] + ij[3][2]*m[2][0] + ij[3][3]*m[3][0];
prod[3][1] = ij[3][0]*m[0][1] + ij[3][1]*m[1][1] + ij[3][2]*m[2][1] + ij[3][3]*m[3][1];
prod[3][2] = ij[3][0]*m[0][2] + ij[3][1]*m[1][2] + ij[3][2]*m[2][2] + ij[3][3]*m[3][2];
prod[3][3] = ij[3][0]*m[0][3] + ij[3][1]*m[1][3] + ij[3][2]*m[2][3] + ij[3][3]*m[3][3];
return prod;
}
|
La multiplicion de matrices, no es conmutativa. Por lo general, AB no es igual que BA. Una excepcion seria el caso de multiplicar una matriz, por la matriz identidad, resultando la misma matriz original, AI = IA = A.
Multiplicar una matriz por la matriz nula, resulta la matriz nula.
Si a una matriz le cambiamos los elementos Aij por Aji (cambiar filas por columnas), habremos obtenido la traspuesta de A, que notaremos como AT. Como resulta logico
imaginar, se cumple que (AT)T = A.
![]()
| Teorema 1[2.2] |
|
Dados dos escalares, a y b, y tres matrices de nxm A, B y C, se cumple lo siguiente:
(a) A + B = B + A (b) (A + B) + C = A + (B + C) (c) a(bA) = (ab)A (d) a(A + B) = aA + aB (e) (a + b)A = aA + bB (f) (AB)T = ATBT |
Una matriz M de n x n (cuadrada) diremos que tiene inversa, llamada M-1, si MM-1 = M-1M = I.
No todas las matrices tienen inversa. A aquellas matrices que no tienen inversa se las llama singulares. Un ejemplo de matriz singular seria aquella que
tubiera una fila y/o columna llena de ceros. Una matriz M es invertible (que tiene inversa) si y solo si MT es invertible.
Llamaremos matriz ortogonal a aquellas que su inversa es igual a su traspuesta, osea, que una matriz es ortogonal, si y solo si M-1 = MT.
Una propiedad interesante de estas matrices, es que mantienen el modulo y el angulo de un vector cuando son usadas para trasformaciones.
Existen otras muchas operaciones, como el determinante, vectores eigen, diagonalizar, etc que no veremos por no ser muy utiles por ahora.
2.3 Transformaciones
El conjunto de operaciones que vamos a necesitar hacer sobre vectores 3D podemos llamarlas transformaciones. Escalar un vector seria un
ejemplo de transformacion lineal, osea que el resultado es proporcional a los datos iniciales. Trasladar vectores no es una transformacion lineal, pero haciendo unos cambios podremos
hacerlo con matrices (que solo sirven para transformaciones lineales).
Esta es la razon de usar vectores y matrices de 4 dimensiones y no de 3. A esta 4 coordenada en nuestros vectores la
llamaremos coordenada homogenea (comunmente designada como w, que normalmente podremos olvidar y que suele valer 1) y al proceso de dividir las 3 coordenadas (x, y y z) por w, lo llamaremos
homogeneizar. Podriamos pensar que este proceso seria como proyectar un vector de dimension N en N-1
Para realizar estar operaciones, nos apoyaremos en las matrices, ya que, cualquier transformacion lineal de Rn a Rm puede ser representada por una matriz de m x n.
Para transformar un vector, que puede ser representado como una matriz de nx1, calculamos Lv, donde L es la matriz de transformacion linear (llamada matriz de induccion) y v es el vector. El
resultado seria un vector en Rm
Las tipicas operaciones de un motor 3D sobre vectores pueden realizarse con matrices de 3×3, menos la traslacion que requiere una de 4×4. Es conveniente usar el mismo tamaño para todas,
asi que usaremos matrices de 4×4. Esto significa que todos nuestros vectores deben ser de 4 dimensiones, y tener la cuarta componente a 1, de lo contrario
los calculos de traslacion fallarian. A esta cuarta coordenada, la llamaremos coordenada w.
2.4 Matriz de escalado
La funcion de escalado multiplica las componentes x, y y z de un vector por un escalar. Podriamos definirla como S(v) = sxv1i + syv2j + szv3k + v41, donde s
es un vector, cuyas componentes son lo que queremos escalar en cada eje. La matriz de induccion de esta transformacion seria:

Podremos escalar cualquier vector haciendo simplemente Sv. Veamoslo con un ejemplo, tenemos el vector v = <2, 3, 4> y queremos escalarlo por s = <0, 1, 2>:

El resultado seria el vector <0, 3, 8>, que corresponde con lo que vimos en la entrega anterior sobre escalado de vectores.
2.5 Matriz de traslacion
La funcion de traslacion mueve en el espacio un vector. Podriamos definirla como T(v) = (v1 + txv4)i + (v2 + tyv4)j + (v3 + tzv4)k + v41.
La matriz de induccion de esta transformacion seria:

Podremos traladar cualquier vector calculando Tv. Si quisieramos trasladar, por ejemplo, el vector v = <1, 4, 2> y queremos trasladarlo por t = <1, 1, 1>:

Obtenemos el vector <3, 6, 4>, que seria lo que hubieramos obtenido usando las formulas del tutorial anterior.
2.6 Matriz de rotacion
En el tutorial anterior, vimos que para rotar un vector sobre los ejes de coordenadas, se calculaba en el eje x:
ynuevo = yviejocos(ß) - zviejosin(ß)
znuevo = zviejosin(ß) + yviejocos(ß)
en el eje y:
xnuevo = xviejocos(ß) - zviejosin(ß)
znuevo = zviejosin(ß) + xviejocos(ß)
y en el eje z:
xnuevo = xviejocos(ß) - yviejosin(ß)
ynuevo = yviejosin(ß) + xviejocos(ß)
Para estos calculos, es util cumplir con la regla de la mano derecha, que viene a decir que apuntes con el dedo gordo de tu mano derecha en la direccion positiva del
eje en el que quieres rotar, y el sentido de tus dedos al cerrarse sobre la mano, indicaran el sentido positivo en la rotacion. Tal como estan expresadas estas
formulas, cumplen justamente lo contrario. Arreglarlas para que cumplan la norma, es facil. En el eje x:
ynuevo = yviejocos(ß) + zviejosin(ß)
znuevo = zviejosin(ß) - yviejocos(ß)
en el eje y:
xnuevo = xviejocos(ß) + zviejosin(ß)
znuevo = zviejosin(ß) - xviejocos(ß)
y en el eje z:
xnuevo = xviejocos(ß) + yviejosin(ß)
ynuevo = yviejosin(ß) - xviejocos(ß)
A partir de estas nuevas formulas, podemos definir las rotaciones en el eje x como:
Rx(v) = v1i + (v2cos(ß) + v3sin(ß))j +
(v2sin(ß) - v3cos(ß))k + v41
en el eje y:
Ry(v) = (v1cos(ß) + v3sin(ß))i + v2j +
(v3sin(ß) - v1cos(ß))k + v41
en el eje z:
Rz(v) = (v1cos(ß) + v2sin(ß))i +
(v2sin(ß) - v1cos(ß))j + v3k + v41
Con lo que la matriz de induccion para la rotacion en el eje x seria:

para el eje y:

y para el eje z:

Lo comprobaremos con un ejemplo. Tenemos el vector v = <4, 0, 0> y queremos rotarlo en el eje z 90º:

Teniendo en cuenta la regla de la mano derecha, que nos indica que angulo crece en el sentido de las agujas del reloj, era facil adivinar que el resultado no podria ser otro
que <0, -4, 0>.
2.7 Matriz de proyeccion
Entendemos por proyectar pasar una geometria 3D a otra 2D (R3 -> R2). Osea de nuestro imaginario mundo 3D a las
tristes 2 dimensiones de nuestras pantallas. Para simplificar el proceso situaremos al observador (nosotros) en el origen de coordenadas y mirando hacia el eje z un solo un punto 3D.
Pensemos en un rayo que una este punto con nuestro ojo pasando por el monitor. El punto donde corta este rayo el monitor, seria el punto proyectado que buscamos:

Gracias a Thales sabemos que:
xs = x * d / z
ys = y * d / z
Osea que a medida que z crece, x e y disminiyen proporcionalmente. Su funcion de proyeccion es P(v) = v1d/v3i + v2d/v3j + dk.
Esta formula no nos vale como dijimos antes. Despues de una serie de transformaciones, obtenemos una que si nos vale P(v) = v1i + v2j + v3k + v3/d1 de la
que ya podemos extraer la matriz de proyeccion:

2.8 Matrix te posee…
Visto todo lo anterior, veamos un sencillo ejemplo para entender la necesidad de usar matrices para nuestros calculos 3D. Imaginemos que tenemos un
modelo 3D al que queremos hacerle n transformaciones. La unidad basica de cualquier geometria 3D, son los puntos (osea vectores) asi que
imaginaremos esa geometria 3D como un conjunto, mas o menos bonito, de vectores. Sin usar matrices, solo tendriamos las formulas de transformaciones del tutorial anterior.
Deberiamos calcular las n transformaciones a cada vector del modelo.
Usando matrices, si queremos hacer n transformaciones, usaremos para ello n matrices de induccion : T1, T2, …, Tn.
Para calcular la primera transformacion, y siendo v el primer punto, haremos T1v. Luego aplicaremos la segunda transformacion, T2(T1v) y
asi con todas las demas, Tn(Tn-1…(T2(T1v))…) que viene a ser lo mismo que Tn…T2T1v.
La multiplicacion de matrices es asociativa, osea, podemos mover los parentesis a nuestro antojo. Asi que tambien podemos escribir lo anterior como
(Tn…T2T1)v. Asi tenemos (Tn…T2T1) por un lado, y v por otro.
Lo primero, despues de multiplarlas (tambien llamado concatenar) todas, es una matriz que multiplandola por v realizara todos los cambios que queriamos sobre el vector y sobre todos los demas de nuestra geometria. Y lo mejor es que solo habremos
calculado (Tn…T2T1) una vez.
Cuidado con cambiar el orden de las transformaciones, ya que la multiplicacion de matrices no es conmutativa.
2.9 Espacios
Cuando usemos un modelo, normalmente lo habremos construido a partir de un fichero en el que estaran todos los datos de su geometria (entre otras cosas). Este fichero lo conseguimos a gracias a
un exportador que posiblemente tendremos que construir nosotros mismos (y que por si solo seria un tema interesante para otros articulos). Los datos de esta geometria (por simplificar solo tendremos vertices)
tienen como eje de coordenadas un punto especifico, generalmente el centro, o la base del objeto (el pivot del 3DSMax por ejemplo). Diremos que estos vertices estan en espacio de objeto (object space)
Si tenemos varios objetos, cada uno tendra su geometria haciendo referencia a su propio espacio. Necesitamos crear un espacio comun a todos para poder situar nuestros objetos en el y crear un entorno.
A este espacio lo llamaremos espacio mundo, world espace (la mayoria de estos terminos seria mejor ni traducirlos…). Para pasar nuestros puntos de un
espacio local al espacio mundo, usaremos una matriz homogenea de 4x4 que llamaremos matriz de transformacion mundo (world transformation matrix).
Cada objeto tendra una, y almacenara cada rotacion, escalado o movimiento.
Ademas de pasar nuestros objetos de un espacio local a otro global, tenemos que pasar nuestros objetos a otro sistema distinto, el del punto de vista del usuario. Podemos imaginarnos que el usuario es una camara que
se va desplazando y rotando. Esto modificara la forma de proyectar en mundo en nuestras pantallas. Mas arriba vimos como hacerlo cuando la camara esta en el origen de coordenadas y mira al eje z, ¿ que pasa cuando el
usuario se mueve ?
Lo primero que se nos ocurriria seria movernos hasta ese punto y calcular unas nuevas formulas de proyeccion. En cambio el profesor Hubert J. Farnsworth (de Futurama) nos diria
que lo mas sencillo es dejar la camara donde esta, y mover el mundo en torno a ella (como hace el para mover su nave). Asi mantendremos la camara en el origen de coordenadas, lo que simplifica los calculos. Si movemos la cabeza hacia arriba, veremos lo mismo que si rotamos el mundo hacia abajo.
Supongamos que tenemos la camara en la posicion v, y que esta orientada segun r (siendo rx, ry, rz los angulos en cada eje, y siendo
rx = ry = rz = 0 la direccion que indica el eje z positivo). Una vez que tenemos todos nuestros objetos en world space, para pasarlos
al espacio vista (view space) tendremos que transladar todo en el world space por -v y rotar todo en el world space por -rx, -ry y -rz.
Una vez hecho esto, solo tendramos que proyectar todo a nuestras pantallas como vimos mas arriba. Usando matrices, todo esto quedaria asi:
P x S x Rz x Ry x Rx x T
El orden en el que hemos hecho las rotaciones, es porque la camara apunta al eje z positivo. No siempre esta claro como elegir estos angulos para una determinada
orientacion, pero eso lo veremos mas profundamente en el siguiente capitulo sobre quaterniones.
Con esto se acaba el tema de las matrices. Podeis bajaros la nueva version GML, que incluye las clases mat3 y mat4.
En la siguiente entrega: quaternions, hasta pronto!!
