Quaterniones
3.0 Introduccion
Un quaternion es una forma alternativa de representar rotaciones a traves del cualquier eje. Matematicamente, son una extension del conjunto de numeros complejos. El primero que hablo de ellos fue William Hamilton, hace cosa como de 100 años.
Presentan varias ventajas comparado con las rotaciones por matrices. Concatenar quaterniones exige menos operaciones, requieren menos espacio para almacenarlos que una matriz, son mas faciles de interpolar que las matrices, etc. Podemos pensar en un quaternion como en un vector 4 dimensional con la forma:
q = { w, x, y, z } = w + xi + yj + zk
Tambien los podemos encontrar representados como: q = s + v, donde s es un escalar que representa la componente w de q,
y v el vector correspondiente a x, y y z que representa su parte imaginaria. Para aprender a operar con quaternions antes debemos refrescar un poco la memoria sobre los numeros imaginarios.
3.1 Planeta Imaginario
Ecuaciones del tipo x2 + 1 = 0 no tienen solucion en el conjunto de los numeros reales, R, donde no existe ningun numero que cumpla x2 = -1. En el conjunto de los numero reales, ningun cuadrado de un numero puede ser negativo. Para resolver este tipo de ecuaciones tenemos que ampliar R con el conjunto de numeros imaginarios, I, donde todos los numeros (incluidos los negativos) tengan raiz cuadrada.
Para hacer posible que el cuadrado de un numero pueda ser negativo, nos sacamos de la manga la unidad imaginaria pura i, que cumple:
i2 = -1
¿ Como es posible esto ? Como diria el profesor Hubert J. Farnsworth, “todo es posible si te lo imaginas“. Asi, la ecuacion x2 + 9 = 0, si tiene solucion en C:
x2 + 9 = 0
x2 = -9
x = sqrt(-9) = sqrt(9) sqrt(-1) = ±3i
De la union de R e I, obtenemos el conjunto de los numeros complejos. Llamaremos numero complejo al par (a, b), donde a representa la parte real y b la parte imaginaria. Ademas cumple:
(a, b) = (c, d), si a = c y b = d
(a, b) + (c, d) = (a + c, b + d)
(a, b) * (c, d) = (ac – bd, ad + bc)
Podriamos representar el numero imaginario puro i como un numero complejo de la forma (0, 1), asi que podremos operar con i siguiendo las reglas de arriba, por ejemplo para calcular potencias de i:
i2 = (0, 1) (0, 1) = (0 – 1, 0 + 0) = (-1, 0) = -1
i3 = i2i = (-1, 0) (0, 1) = -i
i4 = i3i = (0, -1) (0, 1) = 1
Para sumar dos numeros complejos x = ai e y = bi (restarlos seria similar) :
x + y = ai + bi = (a + b)i
Multiplicar los anteriores numeros complejos (y similarmente tambien dividir) seria:
xy = aibi = abi2 = -ab
Para dividir tened en cuenta que i/i = 1.
Existe otra forma de representar a los numeros complejos, y es la forma binomica: a + bi, donde a y b son numeros reales. Asi lo podemos representar es un eje cartesiano:

Si reemplazamos i por -i de un numero complejo z, invertir la coordenada imaginaria, obtenemos el conjugado de z. Si multiplicamos un numero complejo por su conjugado, eliminaremos su parte imaginaria:
(a, b) * (a, -b) = (aa – b(-b), -ab + ba) = (a2 + b2, 0)
El modulo de una numero complejo z = (a + bi), representado como |z|, es:
|z| = sqrt(z * z) = sqrt(a2 + b2)
Como ya vimos anteriormente con los vectores, el modulo de un numero complejo es la distancia del punto (a, b) al origen del sistema cartesiano donde un eje representa su parte real y otro, la imaginaria.
3.2 Numeros hipercomplejos, quaternions y cintas de video
Que no te asuste su nombre, un numero hipercomplejo no es mas que la suma de un numero real y varios numeros imaginarios. El subconjunto de los numeros hipercomplejos que nos interesa a nosotros es el de los quaternions, que tienen esta forma:
q0 + q1i + q2j + q3k
Donde q0, q1, q2 y q3 son numeros reales e i j y k son imaginarios. Si q0 = 0, diremos que tenemos un quaternion puro.
Para multiplicar las partes imaginarias de un numero hipercomplejo, seguiremos las reglas de Hamilton (inventor de los quaternions en 1843), que dicen lo siguiente:
ij = k
jk = i
kj = j
ji = -k
kj = -i
ik = -j
Antes de saber como nos pueden ayudar los quaternions en nuestros juegos, veremos como realizar algunas operaciones basicas con
ellos.
3.3 Suma y resta de quaternions
Sumar y restar quaternions es muy parecido a lo que hemos hecho antes, teniendo 2 quaternions p = p0 + p1i + p2j + p3k y q = q0 + q1i + q2j + q3k seria:
p + q = (p0 + q0) + (p1 + q1)i + (p2 + q2)j + (p3 + q3)k
y la resta
p – q = (p0 – q0) + (p1 – q1)i + (p2 – q2)j + (p3 – q3)k
La verdad es que estas operaciones apenas la usaremos, siendo mucho mas importante la multiplicacion.
3.3 Multiplicar quaternions
Sera la operacion que mas usemos, como veremos mas adelante. Para multiplicar quaternions usaremos la multiplicacion de numeros imaginarios. La codificaremos de la siguiente forma:
quat &quat::operator *(const quat &q)
{
quat t;
t.x = (w * q.x + x * q.w + y * q.z - z * q.y);
t.y = (w * q.y - x * q.z + y * q.w + z * q.x);
t.z = (w * q.z + x * q.y - y * q.x + z * q.w);
t.w = (w * q.w - x * q.x - y * q.y - z * q.z);
return t;
}
|
3.4 Conjugado
Es similar a la forma en la que hizimos el conjugado de numeros complejos. Para hacer el conjugado de un quaternion
simplemente cambiaremos cada parte imaginaria por su negativo. Asi, si tenemos el quaternion q = q0 + q1i + q2j + q3k su conjugado, q*, seria q0 – q1i – q2j – q3k.
El conjugado cumple (pq)* = q*p*. Osea, que el conjugado del producto de dos quaternions es igual al producto de los conjugados, pero en distinto orden.
Otra propiedad interesante es que q*q es igual a q02 + q12 + q22 + q32, que vemos que es un numero real.
3.5 Modulo
El modulo de un quaternion, |q|, es sqrt(qq). Asi que |q|2 = qq.
3.6 Inversa
Si multiplicamos un quaternion por su inverso obtendremos el numero real 1. Para calcular el inverso de un quaternion, llamado q-1, haremos q* / |q|2
3.7 Rotando a Miss Daisy
Despues de ver las operaciones basicas que podemos hacer con quaternions, estamos en condiciones para afrontar lo que nos
interesa de los quarternions, rotar puntos con respecto a cualquier eje. Usaremos los quatarnions casi siempre de la siguiente forma:
- “Meteremos” una rotacion en un quaternion, osea que haremos que represente la rotacion que queremos. Para esto partiremos de un vector, o una matriz, o unos angulos Euler (pronunciado Oiler), que representen esa rotacion que queremos.
- Teniendo ya la rotacion “dentro” del quatarnion, operaremos sobre el este. Normalmente haremos interpolaciones, que son mucho mas faciles de hacer que con matrices y es la verdadera causa de usar quaternions.
- Transformaremos nuestro quaternion es su forma de matriz, para poder seguir con los calculos.
Primero veremos como representar esa rotacion en un quaternion dado un vector que define el eje de rotacion. Tenemos el quaternion q = q0 + q1i + q2j + q3k, podemos pensar en i, j y k como en vectores imaginarios i, j y k. Asi, q1, q2 y q3 serian las componentes de dichos vectores. Asi que nuestro quaternion podria representarse ahora como:
q = q0 + q
Donde q0 es un escalar y q es un vector que representa la direccion en la cual el eje de la rotacion señala. Dada una rotacion ß con respecto al eje u (vector normalizado), el quaternion que representa esa rotacion es:
q = cos(ß/2) + sin(ß/2)u
Sabiendo esto, si queremos representar una rotacion sobre un eje determinado en un quaternion, la funcion que usaremos seria:
quat &quat::fromAngles(const vec3 &axis, real angle)
{
vec3 v;
real s;
v = vec3::normalize(axis);
angle /= 2.0f;
s = calc::sin(angle);
x = v.x * s;
y = v.y * s;
z = v.z * s;
w = calc::cos(angle);
}
|
Ademas de con un vector, tambien nos puede interesar pasarle una matriz para definir la rotacion. La forma de hacer esto la saque de Game Developer Magazine, y la podreis ver en el codigo que acompaña a este capitulo (::fromMatrix()).
Otro metodo es mediente angulos Euler. Mediante este sistema se define una rotacion a traves de tres simples rotaciones de los ejes:

Este grafico vemos como primero se rotaria el sobre el eje z (rotacion azul), luego sobre el nuevo eje x’ (rotacion verde) y finalmente sobre el nuevo eje z” (rotacion roja). Este es el orden mas utilizado, aunque puede haber otras convenciones distintas. Para usar angulos Euler me base nuevamente en una funcion de Game Developer Magazine (::fromEuler())
Ya sabemos como representar una rotacion en un quaternion. Ahora veremos como rotar un punto, vector, teniendo un quaternion con una rotacion ya definida. Definimos el quternion puro p:
p = 0 + r
Donde r es el vector a rotar. Pues bien, la parte imaginaria de la operacion qpq*, es nuestro ansiado vector r rotado.
Podemos aplicar muchas rotaciones en un solo quaternion. Imaginemos que queremos rotar un vector representado por el quaternion puro v por q y luego por p. Como vimos arriba, para calcular lo primero, hacemos qvq*. Para hacer la segunda rotacion, p(qvq*)p*.
Como las multiplicaciones son asociativas, lo anterior puede escribirse tambien como (pq)v(q*p*). Tambien sabemos que (q*p*) = (pq)*, asi que nos queda (pq)v(pq)*. Para simplificar aun mas, llamaremos r al producto pq, con lo que nuestro vector final es simplemente rvr*.
3.8 Interpola esto!
Ya que los quaternions pueden ser representados como vectores, son idoneos para interpolarlos. La mayoria de las utilidades de edicion 3D exportan las animaciones usando quaternions y no matrices de rotacion.
Si queremos optener el frame que esta entre dos de las animaciones que nos dan estos programas, solo tenemos que interpolar esos quaternions. La forma mas simple (aunque no muy util) seria una interpolacion lineal:
q(t) = (1 – t)q1 + tq2
Donde q1 y q2 son los dos quaternions que queremos interpolar y t va de 0 a 1. La funcion q(t) recorre la linea que conecta ambos quaternions (pensemos que son vectores). Si el angulo es grande, esta interpolacion no daria unos resultados muy buenos.
Otro tipo de interpolacion mas interesante es la esferica, o SLERP (Spherical Linear intERPolation), que recorreria ambos quaterniones siguiendo el arco (y no la linea) que une ambos quaterniones (si los dos quaternions estan muy cerca, podemos usar una interpolacion lineal, nadie lo notara…). Seria asi:
q(t) = (q1sin((1 – t)ß) + q2sin(tß) ) / sin(ß)
La implementacion en C la podreis ver mejor en el codigo que acompaña a este articulo.
APENDICE
He incluido la clase estatica calc de calculo matematico. Basicamente es un conjunto de funciones estaticas para resolver operaciones matematicas. He pasado todas las constantes a esa clase. Las funciones de seno, coseno y tangente se resulven mediante una tabla preculculada para hacerlas mas rapidas. En un futuro ire incluyendo mas y sustituyendo algunas por su version en ensamblador para hacerlas aun mas rapidas.
Con esto se acaba el tema de las quaterniones. Si quereis saber mas, os recomiendo el tutorial de Nick Bobick de gamasutra (en ingles). Podeis bajaros AQUI la nueva version GML, que incluye la clase quat y la clase de calculo calc.
Popularity: 33%
#1 por david el 17/09/2009 - 3:24 pm
tienes un error en las reglas de Hamilton, en el renglón tres:
kj=j, debería ser ki=j
muy buen artículo
saljdos
#2 por Pepe Moreno el 05/03/2010 - 12:07 pm
Es curioso, pero al final no cuentas realmente que es un Quaternion, ni las relaciones que tiene con otros sistemas de representar la orientación y la posición de algo.
#3 por martin el 05/03/2010 - 10:13 pm
Si que es curioso.
#4 por Antonio Prieto el 10/04/2010 - 4:44 pm
Aunque habré de repasar matemáticas algún tiempo para asimilarlo y poder usarlo, el tutorial es bueno. Pero lo que es impagable es tu sentido del humor. Hacía tiempo que no me reía tanto, y que haya sido comentando un ladrillo matemático ha elevado al cuadrado el regocijo.
Lo he contado como un chiste a un lego en la materia describiendo la situación, y también se ha reído un rato.
Gracias y un saludo