Quaterniones y Rotación Una sólida introducción sobre Quaterniones y su uso en Ogre.


Introducción


Si estas leyendo esta página, es probable que gruñas, despues de trabajar los objetos en Ogre y te des cuenta de que no rotan como piensas que deberían. Cuando yo comencé con Ogre, comprendía vectores, sabía algo de matrices y nada de quaterniones. Este artículo ha sido compilado como un punto de partida para el uso práctico de quaterniones para las rotaciones en Ogre, saltando la matemática y matrices tanto como es posible.

Usa este hilo en el foro para discutir este articulo o hacer una pregunta.

Una nota a los potenciales editores del wiki: Quaterniones son bastante dificiles de aprender. Cuando pongas un trozo de codigo intenta que cada punto sea claro (ej: explicando porque tenemos que multiplicar un vector por un quaternion). Define que variable se usa y para que, y su tipo. Tambien solo pon codigo que sepas de funciona.

Prerrequisitos


Antes de entrar en los quaterniones, mira algo sobre Angulos de Euler, Matrices y matematica de vectores. Para este documento y para trabajar con Ogre, usaremos el sistema de coordenadas de la mano derecha. Esto significa dos cosas. Primero, consideramos +X como el punto a la derecha del monitor, +Y como el punto superior de nuestro monitor y +Z el punto en que tu monitor te apunta.

Vectores


En matematicas, un vector describe una linea desde un punto inicial dado a otro punto en el espacio 3D. Describe ambos direccion y distancia o magnitud. Esto significa que no es un segmento de linea (que no tiene direccion) y no es un rayo (que es infinito). Tiene aspectos de ambos.

Describe dos puntos que deberian normalmente necesitar de seis valores para el espacio 3D. En Ogre, los vectores estan guardados en tres valores asumiendo que el primer punto es un (0,0,0) y el segundo punto es un desplazamiento relativo. Este es el uso comun de los vectores. Esto da (0,0,0) puede ser la raiz del mundo, o puede ser la base del sistema de coordenadas locales del objeto. Ten cuidado de con que espacio estas trabajando cuando uses transformaciones. Porque los vectores solo tienen tres valores, tambien son usado para definir puntos 3D.

Cuando un vector es normalizado, el vector es escalado a una longitud de 1. Usamos normalizados o vectores unidad cuando deseamos describir solo una direccion. Esto es necesario para obtener los resultados deseados con algunos algoritmos matematicos. Recuerda normalizar tus vectores!.

En Ogre, el uso comun de los vectores esta descrito en un punto unico, definiendo una direccion con un vector unidad, o extrayendo la distancia entre dos puntos.

Aqui hay algunas operaciones comunes con vectores y para que deben usarse:

  • Resta del vector A del vector B genera un tercer vector que contiene la direccion y distancia desde A a B. (En este caso A deberia ser considerado 0,0,0 y el vector a B deberia contener las coordenadas relativas a A, no las coordenadas del mundo).

Bv - Av = Av -> Bv

  • Sumando el vector A y el vector B da el vector C. Si tomas el vector B, y colocas su cola sobre la cabeza del vector A (como dos flechas dibujadas en una ruta), entonces el vector C es el vector desde el comienzo de la ruta (la cola de A) al final de la ruta (la cabeza de B). Equivalentemente, deberias conseguir el mismo vector C linealizando los vectores en el orden opuesto: colocando la cabeza del vector A en la cola del vector B.

  • Multiplicando o dividiendo un vector por un escalar (un unico valor constante) no cambia la direccion, pero solo cambia la distancia o componente de magnitud del vector. Por ejemplo el vector (1,1,1) describe una linea (o punto si el origen es ignorado) que tiene un angulo de 45 grados en el plano XZ(arriba), 45 grados en XY(hacia ti) y 45 grados en el plano YZ (derecha). La longitud de la linea es sqrt(x^2 + y^2 + z^2) o sqrt(3) lo que es 1.73. Ahora si multiplicamos este vector por 2 conseguimos (2,2,2). Esto describe una linea con los mismos angulos. Sin embargo la longitud es ahora sqrt(12) o 3.46. Mirar Vector3::Length() en http://www.ogre3d.org/docs/api/html/classOgre_1_1Vector3.html Vector3.

  • Un producto vectorial de dos vectores proporciona un tercer vector perpendicular a los dos iniciales. Es muy util en computacion. el producto vectorial puede ser calculado para ti por Ogre. Vector3 Vector3::crossProduct(Vector3).

  • El producto de dos vectores nos permite encontrar el angulo entre ellos. Se define como

DP(v1,v2) = (Cos Th) * Len1 * Len2

Donde Len1 y Len2 son la longitud de los vectores. Sin embargo tipicamente deberia normalizar ambos vectores para que sus longitudes sean 1. Esto significa que el producto es el coseno de los angulos entre los vectores. De otro modo:

Th = ArcCos DP(v1,v2) ; donde Len1 = Len2 = 1

Esto tambien se encuentra en el API. Real Vector3::dotProduct(Vector3).

Rotaciones de Euler


El metodo de rotacion al que debes estar mas acostumbrado es usar yaw, roll y pitch. Yaw deberia ser la rotacion de izquierda a derecha, alrededor del eje Y sobre el plano XZ. Yaw es usado cuando conduces un coche. Pitch es una rotacion arriba/abajo alrededor del eje X sobre el plano YZ. Pitch se usa cuando estas volando en un jet arriba o abajo. Roll es la rotacion alrededor del eje Z sobre el plano XY. Roll es literalmente lo que ocurre a tu coche cuando tomas una curva demasiado rapido!.

Las rotaciones de Euler pueden ser utiles y estan soportadas en la API de Ogre. Hay sin embargo problemas. Una es que el orden de las operaciones es importante. Si tu pitch en un objeto es de 90 grados, entonces hacer yaw de 90 grados dara un resultado diferente que si yaw es hecho antes que pitch. Pruebalo.

Otro gran problema es saber como cerrar el gimbal. Un gimbal es un eje de rotacion. Este problema ocurre cuando matematicamente dos ejes de rotacion estan alineados y se cancelan.

Finalmente, los angulos de euler estan limitados en su aplicacion practica. Con esto quiero poner de manifiesto que rotar un objeto arbitrariamente no es muy practico usando yaw, roll y pitch. Son utiles si quieres yaw/pitch la camara como en los juegos fps. Con un truco de sceneNode doble puedes conseguir una camara que usara las operaciones de yaw y pitch sin inducir roll. Sin embargo, cuando tengas un objeto apuntando en el que quieres que otro objeto apunte en una posicion arbitraria, conseguir el yaw individual actual, el roll y el pitch entonces calcula la direferencia necesaria para una rotacion a la nueva direccion no es muy eficiente. Tambien, si quieres hacer una rotacion suave desde la actual apuntando a la nueva direccion? No es muy practico. Entrando en matrices.

Rotaciones con Matrices


Porque trabajar con rotaciones de euler de tres dimensiones arbitrariamente puede producir una larga cantidad de operaciones, las matrices son normalmente usadas para guardar y transformarlas.

Un modo de trabajar con matrices es usar tres, cada una especificando un cierto angulo de desplazamiento desde X, Y y Z. Este es nuestro pitch (rotacion en el eje X), yaw (eje Y) y roll (eje Z). Podemos guardarlos en matrices individuales, diciendo matRotationX, matRotationY and matRotationZ. Cuando hemos decidido los angulos que deseamos rotar y los hemos guardado en nuestras matrices, podemos combinar las operaciones en una matriz de transformacion simplemente multiplicandolos.

matTransform = matRotationX * matRotationY * matRotationZ;

Afortunadamente para nosotros, Ogre implementa una clase Matrix3 para manejar matrices 3x3. La clase tambien tiene el operador * sobrecargado asi que la anterior sentencia funcionara bien en tu codigo. Para rotar un objeto en Ogre necesitas primera crear un quaternion con esta matriz usando el constructor apropiado de quaternion o usando FromRotationMatrix sobre un quaternion.

Nota que la multiplicacion de la matriz no es conmutativa. A * B no siempre es igual que B * A. El orden en que aplicas cada matriz de rotacion es importante. Tambien nota que las matrices como representaciones de angulos de euler estan todavia sujetas al bloqueo del gimbal.

Las matrices tambien pueden ser usadas para manejar rotaciones de eje/angulo. Las matrices anteriores usan conceptos de yaw, roll y pitch. Piensa en estos como rotacion alrededor del eje Y, Z y X, respectivamente. Es posible conseguir cualquier rotacion definiendo un unico y arbitrario eje y un angulo de rotacion. Haciendo esto evitamos el problema del bloqueo del gimbal.

Hay un problema con ambos las matrices de angulo de euler y las matrices eje/angulo. Se llama drift de matriz, que sera descrito en la seccion de quaternion bajo la lista de beneficios.

Rotaciones de Quaterniones


Los Quaterniones son un concepto interesante probablemente tan nuevo para ti como lo fueron para mi la primera vez que empece con ellos. Como describi antes, cuando trabajo con quaterniones encuentro facil el dejar de pensar en yaw, pitch y roll. Por ejemplo, decimos que queremos modelar un jet. El jet apunta hacia el eje Z. En terminos de euler la rotacion deberia llamarse roll. En quaterniones esta rotacion alrededor de un vector apunta al eje Z, o la rotacion alrededor del Vector3::UNIT_Z como nos hemos referido en Ogre.

Un quaternion esta compuesto de cuatro componentes: un vector con x, y, z coordenadas y una rotacion w. Esto es una representacion eje/angulo lo he tocado al final de la seccion matriz. La matematica del Quaternion puede ser muy elaborada, incluso incorporando numeros imaginarios. Afortunadamente no necesitamos usar toda esa matematica, no tenemos que comprenderlo para trabajar con rotaciones.

Ahora decimos que tenemos un vector desde (0,0,0) a (1,1,-1). Con nuestro dedo apuntando hacia la izquierda, apuntando 45 grados arriba y 45 grados desde ti.
Ahora rota el boligrafo alrededor de tu dedo, manteniendolo perpendicular todo el tiempo. Nota como el angulo del eje que hemos escogido crea una rotacion diferente.

Como las matrices, podemos combinar las rotaciones de quaterniones multiplicandolos. Sin embargo todavia no son conmutativos. Q1 * Q2 != Q2 * Q1. Ademas el orden de la aplicacion todavia es importante. Tambien como las matrices que representan la rotacion eje/angulo los quaterniones evitan el bloqueo gimbal.

Beneficios de los Quaterniones


Los quaterniones tienen ventajas sobre las matrices. hay Pros y contras como son el numero de multiplicaciones, o adiciones durante diferentes operaciones, etc que hemos discutido en el foro. Cosiderando los beneficios practicos, aqui esta una lista:

  • La representacion eje/angulo evita el bloqueo gimbal.
  • Modificar una rotacion es facil. Digamos que tenemos una rotacion describiendo un -45 grados yaw (giro a la izquierda). Creando un nuevo cuaternion describiendo una yaw de 10 grados (giro a la derecha) y multiplicando los dos, ahora tenemos una rotacion describiendo una vuelta de -35 grados. Deberas usar esto cuando se aplique la misma rotacion a un numero diferente de objetos, o incluso multiples veces al mismo objeto (ej: el jugador). El factor de rotacion incrementa o decrementa dependiendo de las circunstancias.
  • Evita el alineamiento de la matrix drift. Este drift ocurre cuando realizamos muchas operaciones sobre matrices usando precision finita de punto, lo que es usado en tu computadora. Redondeando a numeros reales gradualmente. Las anormalidades en la rotacion comenzaran a aparecer debido a este drift. La matriz debe ser normalizada para resetear eso, sin embargo esto es una operacion costosa. Quaterniones por el otro lado pueden tener este drift, pero es mucho mas barato normalizarlos teniendo solo 4 valores en vez de 9 o mas.
  • La interpolacion de las rotaciones. Cuando rotamos un objeto, queremos que el objeto rote suavemente durante el tiempo. Nos gustaria decir, "Desde tu orientacion actual, rota para apuntar a este punto. Pero toma cerca de 300 frames hacerlo, asi que solo mueve 1/300 de la rotacion ahora." La interpolacion con matrices con es posible?. Los Quaterniones proporcionan dos metodos de interpolacion: linear(slerp) y cubica(squad). Ogre proporciona tres implementaciones: slerp, nlerp y squad. Linear interpolacion nos permite especificar el porcentaje de interpolacion entre los dos cuaterniones. Linear significa que tu "velocidad" de movimiento es constante, lo que te hara sentir mareado si lo usas para una camara u objeto. Slerp y nlerp son ambos lineares. Slerp es mas preciso, pero mas lento. La interpolacion Squad o cubica permite especificar cuatro cuaterniones a,p,q,b. P y Q son usados para definir una curva de interpolacion "velocidad" entre puntos A y B. Esto nos permitira incrementar la velocidad lentamente, mantenerla constante, entonces decrementarla durante la interpolacion. Mirar la seccion de recursos externos.
  • Ogre usa quaterniones! Por eso probablemente este leyendo esta pagina. A menudo, necesitaras conseguir la orientacion actual de un objeto, que se devolvera como un quaternion.

Los quaterniones mas utiles son (normalizados)


w x y z Descripcion
1 0 0 0 Identidad del quaternion,
0 1 0 0 180' giro alrededor del eje X
0 0 1 0 180' giro alrededor del eje Y
0 0 0 1 180' giro alrededor del eje Z
sqrt(0.5) sqrt(0.5) 0 0 90' rotacion alrededor del eje X
sqrt(0.5) 0 sqrt(0.5) 0 90' rotacion alrededor del eje Y
sqrt(0.5) 0 0 sqrt(0.5) 90' rotacion alrededor del eje Z
sqrt(0.5) -sqrt(0.5) 0 0 -90' rotacion alrededor del eje X
sqrt(0.5) 0 -sqrt(0.5) 0 -90' rotacion alrededor del eje Y
sqrt(0.5) 0 0 -sqrt(0.5) -90' rotacion alrededor del eje Z

Estos numeros vienen de esta propiedad:

[w, x, y, z] = [cos(a/2), sin(a/2) * nx, sin(a/2)* ny, sin(a/2) * nz]

Donde a es el angulo de rotacion y es el eje de rotacion.

Preguntas


Esta seccion es para preguntas o simples o de dos lineas de codigo snippets para responder a las preguntas. Mas ejemplos de codigo en la siguiente seccion.

Quaterniones


Comenzando, aqui hay algunos cosas que puedes hacer en Ogre con los quaterniones. Esta seccion tambien tiene algunas clarificaciones del otro texto. Los ejemplos de codigo en la siguiente seccion te ayudaran con problemas mas dificiles.

P. ¿Cómo hago para que mi objeto rote y apunte a otro punto?

R. Un modo es usar el metodo SceneNode::lookAt. Sin embargo deberia tener resultados inesperados cuando se usa como un objeto. Por contra, estamos hablando sobre quaterniones ahi.

Otro modo es a traves de especificar las rotaciones individuales y entonces multiplicarlas juntas.
Rotar alrededor del eje Z 90 grados:

Quaternion q(Degree(90), Vector3::UNIT_Z);
Siguiente, rota alrededor del eje X 90 grados.

Quaternion r(Degree(90), Vector3::UNIT_X);
Rota el objeto con ambas rotaciones en el orden anterior. mNode es un SceneNode:

q = q * r;
mNode->rotate(q);
Un mejor modo es usar la funcion Vector3::getRotationTo(Vector3) para generar un quaternion por nosotros. Mira los ejemplos de código para ver como será usado.

P. Cuando estoy depurando, imprimo los valores individuales x, y, z, w , pero no puedo hacer cabezas y colas de ellos. Que significan?

R. X, Y, Z describe un vector que es el eje de rotacion. En mi proyecto actual, tengo solo la rotacion del plano XZ, lo que significa que tengo cero en cualquier rotacion con pitch o roll. El ejemplo de codigo de debajo muestra como hacer esto. Ademas mis valores se pareceran a esto 0, -.599, 0. Esto describe el eje Y, como es un unico valor. Actualmente esta apuntando hacia abajo como es negativo, y no estoy seguro de porque. Corregire esto cuando aprenda porque. Debe de ser un error en mi codigo.
W es la cantidad de rotacion alrededor del eje vector. Es un valor impar en si mismo. Esto es asi porque es el cos(theta/2), donde theta es el angulo. Deriva el angulo como este, porque produce un Ogre::Radian:

2*Math::ACos(quat.w))

Asi que mi ejemplo de quaternion se parecera a esto con W en los grados:
(0.000,-0.599,-0.000, 73.602). La razon es que Y no es 1 o incluso -1 es porque el quaternion esta normalizado. La normalizacion incluye w,x,y y z, asi que todos ellos necesitan dar algunos igual a 1. De acuerdo 73 es la derivada asi que no es una componente de 1.

P. Veo vectores y quaterniones multiplicados juntos en el codigo. que significa?

R. Multiplicar un quaternion por un vector nos da un vector describiendo el desplazamiento rotacional desde ese vector (un vector rotado). Por ejemplo cuando uno carga el archivo robot.mesh, por defecto apunta su cara a UNIT_X. Cuando ha sido rotado, puede conseguir su orientacion en quaternion por medio de mNode->getOrientation(). Si entonces lo multiplicamos por Vector3::UNIT_X, conseguiremos un vector apuntando en la direccion en la que el robot esta apuntando.

P. Cuando usar setOrientation() frente a rotate()? Cuando multiple los quaterniones contra las orientaciones o vectores? Estoy confundido!

R. SceneNode::getOrientation() devuelve un quaternion describiendo cuanto del desplazamiento rotacion el objeto ha adquirido desde su orientacion de identidad (o la direccion en que comienza a apuntar). getWorldOrientation() devuelve su propio desplazamiento de rotacion, ademas que es de su padre. Si el nodo padre es la raiz, estas funciones devuelven la misma cosa.

Vector3::getRotationTo(Vector3) devuelve un quaternion describiendo una rotacion relactiva desde un vector al siguiente. Recuerda que los quaterniones puede ser pensados como vectores con un angulo acoplado? Recuerda que el Vector A menos el Vector B da el Vector C, un vector relativo desde B a A? La misma cosa con los quaterniones. Piensa en las orientaciones como absolutas, o relativas a la identidad. Piensa en las rotaciones como relativas, usualmente relativas a la orientacion actual.

SceneNode::setOrientation(Quaternion) hace exactamente eso. Lo establece basandose en la direccion en la que apuntan los objetos originales. SceneNode::rotate(Quaternion) establece la orientacion basandose en la orientacion actual. Ademas rotate(q) es equivalente a setOrientation(q * getOrientation()).

Si tenemos un nodo de escena y una rotacion calculada desde getRotationTo, solo necesitamos usar la funcion rotate(quat). De hecho si usamos setOrientation, estaremos rotando mas de lo deseado.

y que hay de slerp con rotaciones y orientaciones?

Cuando comence jugando con slerp, estuve slerping como pone aqui:

mRotSrc = mNode->getOrientation();

mRotDest = src.getRotationTo(mDirection);

...

mNode->rotate(Quaternion::Slerp(mRotProgress, mRotSrc, mRotDest));

Mi pobre robot estuvo girando y girando como en el juego asteroides. He cometido dos errores antes. Puedes senyalarlos?
1) El primer error es que Slerp toma dos orientaciones, no rotaciones relativas. mRotDest es solo una rotacion relativa desde el mRotSrc a mDirection (donde quiero que apunte). Deberia ser capaz de establecer setOrientation(mRotSrc) o setOrientation(mRotDest) y tener mi objeto apuntando a la funete o destino precisamente. Que no podria ocurrir con estos valores. El arreglo es:

mRotDest = src.getRotationTo(mDirection) * mRotSrc;

2) El siguiente error es trabajar con rotaciones absolutas (absolutas al objeto identidad o punto de comienzo), esto es lo que devuelve. Para arreglar esto:

mNode->setOrientation(Quaternion::Slerp(mRotProgress, mRotSrc, mRotDest));

Este es el mismo problema: Un descomprension sobre Slerp. Cuando trabajamos con estas herramientas, considera si estas trabajando en rotaciones relativas o orientaciones.

Unos pocos puntos redundantes:

  • quaternion * vector es un vector rotacional de desplazamiento por el quaternion
  • quaternion * quaternion es un quaternion con ambas rotaciones combinadas, "por lo tanto:"
  • rotation1 * rotation2 es un quaternion con ambas rotaciones, rotation1 es la orientacion del nodo padre y la orientation2 es la orientacion del nodo hijo "y"
  • setsOrientation(getOrientation() * rotation) == rotate(rotation)

P. Por que mientras rota el objeto da un salto?
Estoy usando Slerp para interpolar entre dos orientaciones, y llamando a setOrientation() con el quaternion interpolado.

R. Quaternion::Slerp(), Quaternion::nlerp() etc. tiene un "shortestPath" parametro que por defecto es false. Asegurate de que es true, de otro modo la interpolacion ocasionalmente dara una ruta mayor, lo que resultara en unos pocos frames completamente descontinuos en orientacion.

P. ¿Por qué los tutoriales y la gente del foro sugiere usar dos scene nodes para acoplar mi cámara?

R. La recomendacion es crear un nodo de escena, es decir un "CamNode" y un nodo de Pitch, es decir un "PitchNode". "PitchNode" esta acoplado a "CamNode" y la camara esta acoplada a "PitchNode". El movimiento y Yaw son aplicados al CamNode. Pitch es aplicado al "PitchNode, solo. La razon para este es porque uno puede generar roll a traves de angulos de euler de pitch y yaw. A menudo usa el raton para controlar el angulo de mira de la camara. Se espera mover el raton arriba para mirar arriba, a la derecha para mirar a la derecha , abajo para mirar abajo. Sin embargo esto es lo mismo para pitch up, yaw right y pitch down. Intenta esto con un objeto desk. Esta secuencia induce roll. Usando dos nodos separados dandote el equivalente de un disco plano que flota alrededor. Acoplado a la parte inferior del disco esta una camara que puede mirar solo arriba o abajo. Rotando el disco y moviendo la camara arriba o abajo puedes ver cualquier angulo. El Yaw en el disco afecta a la camara, pero el pitch en la camara no afecta al disco. Esto proporciona el efecto que la mayoria de la gente espera.

Alternativa A. Para un Camara simpre libre puedes usar un nodo y solo rotar en el espacio las coordenadas apropiadas. Una vez que quieras comenzar a mezclar "roll" en esto, debes volver para completar las rotaciones del espacio local--- en este punto no estas probablemente intentando hacer una Camara con movimiento libre para propositos de depuracion.

node->pitch( mPitch, Node::TS_LOCAL );
node->yaw( mYaw, Node::TS_WORLD );
node->translate( mTranslate, Node::TS_LOCAL );


El efecto de esto es pitch el scenenode relativo a si mismo, pero para yaw el scenenode relativo al mundo. Esto significa que el yaw es siempre horizontal y ningun roll se induce por los scenenodes del eje z. ej, la direccion de este esta apuntando.

Plantillas de Codigo

Rotacion de Objetos en el plano XZ


P. Tengo un personaje que anda por una ruta de puntos sobre un plano. Como tengo que hacer para que rote la cara en la direccion en la que anda?

R. Lo primero de todo, necesitamos un vector inicial que apunte a la malla. Cuando cargues tu malla en Ogre, en que direccion deberia estar apuntando, ante de hacer cualquier transformacion? La camara comenzara a apuntar hacia abajo Vector3::NEGATIVE_UNIT_Z . Tambien puedes cargar la malla en tu modelador, pero ten cuidado de que los ejes del modelador no son necesariamente alineados con Ogre. Para este codigo, el vector inicial apuntador es Vector3::UNIT_X .

Esto esta adaptado del Tutorial Intermedio 1:

Vector3 mDestination = mWalkList.front(); // mDestination es la siguiente localizacion
Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (mirar las preguntas anteriores)
Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // mirar (1)
Real mDistance = mDirection.normalise(); // desplegar la distancia, solo queremos la direccion
Quaternion quat = src.getRotationTo(mDirection); // Consigue una operacion de rotacion del quaternion
 
mNode->rotate(quat); // Rota el objeto actual


(1) De los comentarios, getOrientation() devuelve un Quaternion describiendo la rotacion de los objetos en relacion a la raiz. Multiplicandolo por UNIT_X, lo que son los objetos inciales que apuntan a esta posicion, proporcionando un vector que describe su direccion actual, a la que apunta.

Esto funciona porque nuestro objeto no ha sido roll o pitch. Todos los puntos existentes estan en un plano asi que no hay nunca cualquier pitch o roll para que sea enrollado en el quaternion.

P. Lo anterior funciona bien cuando tengo un plano. Ahora tengo un objeto caminando arriba y abajo. Cuando rota comienza a roll y pitch todo en direcciones divertidas. Como puedo tenerlo siempre con la cabeza levantada?

R. Podemos adaptar lo anterior al despliegue de todas las componentes Y antes de que ellos se vuelvan en quaterniones. Esto significa que solo hace rotacion en el plano X/Z.

Vector3 mDestination = mWalkList.front( ); // mDestination es la siguiente localizacion
Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (mirar las preguntas de vector anteriores)
Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // Orientacion desde la direccion inicial
src.y = 0; // Ignora el pitch de la diferencia de angulo
mDirection.y = 0;
src.normalise();
Real mDistance = mDirection.normalise( ); // Ambos vectores son modificados y renormalizados
Quaternion quat = src.getRotationTo(mDirection);
 
mNode->rotate(quat);


P. Algunas veces mi programa sale y consigue una asercion - Division por Zero. Que Ocurre?

R. Esto ocurre debido a una peculiaridad con la rotacion de quaterniones por 180 grados. Cuando le dices a Ogre que rote 180 grados, hay un numero infinito de rutas posibles en las que la rotacion puede ocurrir. Podria ocurrir que cogiera una que no quieres (ej: rotar a traves del suelo), las aserciones de Ogre. Deberias hacer la seleccion dentro de tu codigo como esto:


Vector3 mDestination = mWalkList.front( ); // mDestination es la siguiente localizacion
Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (mirar las preguntas de vector anteriores)
Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // Orientacion desde la direccion inicial
src.y = 0; // Ignora el pitch de diferencia de angulo
mDirection.y = 0;
src.normalise();
Real mDistance = mDirection.normalise( ); // Ambos vectores son modificados y renormalizados
 
if ((1.0f + src.dotProduct(mDirection)) < 0.0001f) // Funciona alrededor de 180 grados de rotacion de quaterniones
{
    mNode->yaw(Degree(180));
}
else
{
    Quaternion quat = src.getRotationTo(mDirection);
    mNode->rotate(quat);
}


Rotación Suave



P. ¿Cómo puedo hacer que mis objetos roten suavemente? mencionastes slerp, etc?

R. Necesitaras tener dos lugares en tu codigo de rotacion, y un area para compartir las variables. Para mi codigo, tengo una clase que maneja todos mis objetos del mundo, asi que el siguiente codigo se encuentra en una clase, pero en metodos diferentes. Slerp y nlerp son intercambiables aqui. Solo cambia el nombre. La diferencia es que slerp es mas preciso, pero necesita mas proceso. Este codigo estas comprobado por el autor.


variables compartidas:


Ogre::Quaternion mOrientSrc; // Orientacion Inicial
Ogre::Quaternion mOrientDest; // Orientacion Destino
Ogre::Real mRotProgress; // Cuanto hemos interpolado
Ogre::Real mRotFactor; // Paso de la interpolacion
boolean mRotating;



Lo siguiente, la rotacion esta iniciada asi. "frames" es un entero en este caso que describe el numero de frames para rotar. Usa tu propia formula para mostrar la longitud del tiempo que quieres rotar.


mRotating = true;
mRotFactor = 1.0f / frames;
mOrientSrc = mNode->getOrientation();
mOrientDest = quat * mOrientSrc; // Queremos la orientacion dest, no una rotacion relativa (quat)
mRotProgress = 0;

Finalmente, durante un metodo de actualizacion llamado por el metodo frameStarted:

if(mRotating) // Tiempo procesado por la rotacion
{
    mRotProgress += mRotFactor;
    if(mRotProgress>1)
    {
        mRotating = false;
    }
    else
    {
        Quaternion delta = Quaternion::Slerp(mRotProgress, mOrientSrc, mOrientDest, true);
        mNode->setOrientation(delta);
    }
} // if mRotating

P: ¿Has mencionado squad?

R: Squad te permite aplicar interpolacion cubica en vez de interpolacion linear. Esto significa que la tasa de turnos se incrementara o decrementara dependiendo de la curva bezier/hermite dada a la funcion. Tendras que experimentar con los coeficientes dados como los primeros parametros de slerp en IntA e IntB. Estos quaterniones son rotaciones intermediarias entre la fuente y el destino. Le he dicho que interpole sobre la curva bezier con mOrientSrc -> mOrientDest, y usa mOrientIntA y mOrientIntB como puntos de curva intermediarios. basicamente experimenta para ver que sucede.

Para usarlo haz los siguientes cambios a la seccion anterior:

Variables compartidas:

+Ogre::Quaternion mOrientIntA;
+Ogre::Quaternion mOrientIntB;

Seccion de iniciacion (modifica los valores .3/.5 para ajustar los puntos intermediarios):

mOrientDest = quat * mOrientSrc;
+mOrientIntA = Quaternion::Slerp(.3, mOrientSrc, mOrientDest, true);
+mOrientIntB = Quaternion::Slerp(.5, mOrientSrc, mOrientDest, true);
mRotProgress = 0;

seccion frameStarted:

-Quaternion delta = Quaternion::Slerp(mRotProgress, mOrientSrc, mOrientDest, true);
+Quaternion delta = Quaternion::Squad(mRotProgress, mOrientSrc, mOrientIntA, mOrientIntB, mOrientDest, true);
mNode->setOrientation(delta);

P. Bien. ¿Ahora cómo puedo usar las rotaciones suaves en dos ejes?
R. Por ejemplo, supon que quieres pitch y yaw, pero saltar el roll mientras estas usando Slerp. Puedes usar una matriz para conseguir los angulos de Euler y eliminar el componente de roll directamente.

Veamos como puedes tener tu mOrientDest

Radian yRad, pRad, rRad;
Matrix3 mat;
mOrientDest.ToRotationMatrix(mat);
mat.ToEulerAnglesYXZ(yRad, pRad, rRad);


Ahora actualiza las componentes de pitch y yaw, y elimina roll

pRad +=Ogre::Degree(0.8 * timeSinceLastFrame);
yRad +=Ogre::Degree(0.8 * timeSinceLastFrame);
rRad = Ogre::Degree(0);

Actualiza la matriz y reconvierte tu mOrientDest

mat.FromEulerAnglesYXZ(yRad, pRad, rRad);
mOrientDest.FromRotationMatrix(mat);

Todo tambien es lo mismo

delta = Quaternion::nlerp(mRotProgress, mOrientSrc, mOrientDest, true);
mCamera->setOrientation(delta)

Reestableciendo la Orientacion


P. ¿Cómo puedo hacer que mis objetos permanezcan hacia arriba despues de una rotacion de inclinacion?

R. Recuerda que un objeto tiene tres conjuntos de ejes. Los ejes de mundo, los ejes padres y los ejes locales. Es tambien referencia como los Espacios de Transformacion (mirar SceneNode). Asumiendo el eje positivo Y que esta mirando hacia arriba en el objeto, todo lo que necesitamos hacer es mostrar como de lejos eje Y del objeto tiende al eje Y del mundo. Nota, podriamos tambien aplicar este mismo principio, si queremos que el objeto permanezca mirando hacia arriba relativamente a su padre, solamente. Imagina el espacio (mundo), con una nave espacial (padre) y un objeto caminando de acuerdo a su padre, no al mundo. Finalmente, simplemente rotamos a ese vector en cada instante o usamos un slerp. Una cosa mas que notar es que hay que para alinear el eje Y, debemos rotar en el padre o en el espacio de transformacion del mundo, no el espacio local.

// Conseguir rotar el vector hacia arriba (asumido UNIT_Y)
Vector3 localY = mNode->getOrientation() * Vector3::UNIT_Y;
Quaternion quat = localY.getRotationTo(Vector3::UNIT_Y);
mNode->rotate(quat, Node::TS_PARENT);


P. ¿Cómo puedo hacer que mis objetos vuelvan a su direccion inicial(en la que estaban mirando)?

R. Ya que tenemos que saber el vector inicial al que se apunta de la malla para rotarlo apropiadamente, solo tenemos que rotarlo otra a la inversa. mInitFacing es un Vector3 que apunta hacia abajo en la direccion inicial de las caras de la malla que se cargaron, tales como Vector3::UNIT_X.


// Conseguir la rotacion a la posicion original
Vector3 currentFacing = mNode->getOrientation() * mInitFacing;
Quaternion quat = currentFacing.getRotationTo(mInitFacing);
mNode->rotate(quat);

Problemas y soluciones


Problemas con la implementacion de las sugerencias de los articulos y soluciones para arreglarlos. Incluso si no tienes estos problemas te pueden ayudar a comprender los tutoriales.

Primer problema: El personaje se dio la vuelta ocasionalmente.

Solucion: Haz el siguiente paso que sugiere consiguiendo el componente Y incluso si el personaje esta sobre el plano porque el personaje no esta en un plano de igual nivel con el otro plano (mi razonamiento, no es necesariamente correcto).

Segundo problema: El personaje fue rotado incorrectamente de una manera inconsistente.

Solucion: Usa rotate en vez de setOrientation porque estas son dos funciones muy diferentes , puedes rotar multiplicando la orientacion anterior por el quaternion en que tu quieras rotarlo.


Ver También

external image dl1714

  • La Clase Angulo de Euler

  • El API de Ogre - Usalo. Vivelo. Amalo.

    • SceneNode - Donde tus objetos estan acoplados, es rotado.
    • Radian
    • Degree
    • Vector3
    • Matrix3
    • Quaternion

  • En la lista de lectura recomendada, Snide recomienda el libro, "3D Math Primer for Graphics and Game Development" por Fletcher Dunn, Ian Parberry ISBN 1-55622-911-9. El autor de este articulo no lo ha revisado. La tabla de contenidos parece interesante con capitulos sobre quaterniones, matrices, vectores y otros conceptos de matematica 3D. Ellos tambien tienen un sitio con errata, un demo y otra informacion.

  • Una introduccion a la Representacion de Rotaciones en Aritmetica de Quaterniones - Mira este sitio para ver matematica de quaterniones. Tambien encontraras descripciones mas detalladas de SLERP y SQUAD ya que ha nuestra API le falta.

  • Matriz y Quaternion FAQ - Preguntas y Respuestas.

  • Quaternion Spell - Algunas cosas utiles que puede ser hechas con quaterniones, ambas en el modelador XSI y en general.

  • Comprendiendo Slerp, entonces no usandolo - Porque nlerp es a menudo mejor eleccion que slerp.

  • Quaternion en SecondLife Wiki

  • Quaternion on EuclideanSpace

  • Calculador en linea de Quaterniones