UNIVERSIDAD CENTRAL DE VENEZUELA FACULTAD DE CIENCIAS ESCUELA DE COMPUTACIÓN INTRODUCCIÓN A LA COMPUTACIÓN GRÁFICA TALLER #2: Quaternions Los Quaternions es una forma alternativa de representar rotaciones a través de cualquier eje. Matemáticamente, son una extensión del conjunto de números complejos. Presentan 4 dimensiones, que se obtienen al mezclar un vector y un escalar. Estos se encuentran en el espacio R4. Se pueden representar de dos maneras: Q = {x,y,z,w} donde Q es el quaternion a representar, {x,y,z} son los valores correspondientes del eje a rotar y w sería un escalar que es la magnitud a rotar, la otra representación es una matriz 4x4 que nos servirá para utilizar en OpenGL. ¿Por qué usar Quaternions? Presentan varias ventajas comparado con las rotaciones por matrices: - Pueden representarse en 4 números, a diferencia con los 9 números de las matrices de rotación. - Al ejecutar rotaciones, ciertos errores se acumulan después de una cantidad. En los Quaternions, estos errores tardan mucho más en acumularse que en las rotaciones matriciales. - Se pueden convertir a manera matricial de una forma trivial. - Similar a las matrices de rotación, se pueden multiplicar Quaternions para representar múltiples rotaciones en un elemento. Veamos ciertas operaciones de los Quaternions: Conjugada: La conjugada de los quaternions, es sencillamente pasar a negativo la parte vectorial de ellos, así que teniendo el Q={x,y,z,w} su conjugada es Q’={-x,-y,-z,w} Quaternion Quaternion::conjugada() { Quaternion Q(-x,-y,-z,w); return Q; } Normalizar: es similar a normalizar un vector, obtenemos un quaternion unitario (magnitud 1). void Quaternion::normalize() { float magnitude=(x*x) + (y*y)+(z*z)+(w*w); magnitude=sqrt(magnitude); w=w/magnitude; x=x/magnitude; y=y/magnitude; z=z/magnitude; } Multiplicación de dos Quaternions: cuando uno multiplica dos quaternions, lo que obtiene es la rotación acumulada de ambos. Vale acotar que multiplicar Q1*Q2 es distinto a multiplicar Q2*Q1 ya que el orden de aplicar las rotaciones afecta el resultado. Quaternion Quaternion::operator* (Quaternion &q) { Quaternion Q(w*q.x + x*q.w + y*q.z - z*q.y, w*q.y + y*q.w + z*q.x - x*q.z, w*q.z + z*q.w + x*q.y - y*q.x, w*q.w - x*q.x - y*q.y - z*q.z); return Q; } Convertir un quaternion en una matriz: Este método consiste en transformar el quaternion de 4 dimensiones en una matriz de rotación 4x4, la cual le permite a OpenGL utilizarla por medio de la instrucción glMultMatrixf(), con lo que lograremos multiplicar el quaternion de por la rotación que deseamos. void Quaternion::CreateMatrix(float *pMatrix) { if(pMatrix) { // First row pMatrix[ 0] = 1.0f - 2.0f * ( y * y + z * z ); pMatrix[ 1] = 2.0f * ( x * y - w * z ); pMatrix[ 2] = 2.0f * ( x * z + w * y ); pMatrix[ 3] = 0.0f; // Second row pMatrix[ 4] = 2.0f * ( x * y + w * z ); pMatrix[ 5] = 1.0f - 2.0f * ( x * x + z * z ); pMatrix[ 6] = 2.0f * ( y * z - w * x ); pMatrix[ 7] = 0.0f; // Third row pMatrix[ 8] = 2.0f * ( x * z - w * y ); pMatrix[ 9] = 2.0f * ( y * z + w * x ); pMatrix[10] = 1.0f - 2.0f * ( x * x + y * y ); pMatrix[11] = 0.0f; // Fourth row pMatrix[12] = 0; pMatrix[13] = 0; pMatrix[14] = 0; pMatrix[15] = 1.0f; } } Aplicar rotación a Quaternion: Dando el eje y el ángulo de rotación y el ángulo, el siguiente algoritmo sirve para generar un quaternion: void Quaternion::CreateFromAxisAngle(float *vector, const float &in_degrees) { float angle = float((in_degrees / 180.0f) * PI); float result = float(sin(angle/2.0f)); w = float(cos(angle/2.0f)); // Calculate the x, y and z of the quaternion x = float(vector[0] * result); y = float(vector[1] * result); z = float(vector[2] * result); } Donde vector es un vector de 3 posiciones, indicando en que eje se va a rotar y in_degrees es la rotación a aplicar. Es necesario normalizar el quaternion después de aplicar una rotación, por si acaso hay valores muy cercanos a cero. OBJ Object File Format (OBJ) es un formato de data simple que representa geometrías en 3D, nombrando la posición de cada vértice, las coordenadas UV de cada textura, las normales, y las caras que hacen a cada polígono definida por la lista de vértices. Los vértices deberían ser guardados en contra de las agujas del reloj (counter-clockwise), haciendo la declaración de las normales innecesarias. El formato, acepta comentarios. La línea es comentada (o el resto de la línea) usando el símbolo de numeral (#). Los vértices empiezan definidos con la letra “v”, las texturas con las letras “vt”, las normales con las letras “vn” y las caras con “f”. En las caras, los vértices, las coordenadas de textura y las normales están separadas por el carácter slash (/) ordenados respectivamente y sin espaciados entre los slash separadores. Como las coordenadas de textura y las normales son opcionales de colocar, entonces pueden o no aparecer en la cara: - - - Vértices: Un vértice válido tiene como valor mínimo 1 y le corresponde algún valor de la lista de vértices (f 1 2 3) Vértices/Coordenadas-de-textura: Opcional, la coordenada de textura tiene también un valor mínimo 1 y corresponde con algún valor de la lista de coordenadas de textura (f 1/1 2/4 3/6 4/8). Vértices/Coordenadas-de-textura/Normal: Opcional, la normal tiene un valor mínimo de 1 y debe corresponder con algún valor de la lista de normales (f 2/3/4 5/1/3 3/6/2). Vértice//Normal: Opcional, aquí no tiene definida una coordenada de textura pero si una normal (f 1//3 2//6 3//9). Un OBJ contiene varios tipos de definición. El siguiente cuadro muestra el formato ejemplo: # Lista de vértices, con (x,y,z[,w]) coordenadas, w es opcional y por defecto es 1.0. vxyzw v ... ... # Lista de coordenadas de textura, en (u[,v][,w]) coordenadas, ‘v’ y ‘w’ son opcionales y #por defecto son 0. vt u v vt ... ... # Lista de normales en formato (x,y,z); las normales pueden no ser unitarias. vn x y z vn ... ... # Definición de caras f v1 v2 v3… f v1/vt1 v2/vt2 v3/vt3… f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3… f v1//vn1 v2//vn2 v3//vn3… ... Ahorita se muestra un ejemplo de una definición de un cuadrado sin normales y sin coordenadas de textura: v 10.0 -50.0 -10.0 v 10.0 50.0 -10.0 v -10.0 -50.0 -10.0 v -10.0 50.0 -10.0 f314 f412 Destacamos nuevamente que un archivo OBJ posee más información que un OFF al tener coordenadas de textura y normales. También posee información de control como donde está la imagen de las coordenadas de textura, donde obtener los materiales de los vértices, los nombres de los grupos y objetos y muchas otras cosas. Para la realización de la tarea #2, no es necesaria nada de esta información, solo los vértices y las caras. Referencias: Matrix And Quaternions FAQ http://web.archive.org/web/20041029003853/http:/www.j3d.org/matrix_faq/matrfaq_latest.html OpenGL Tutorials: Using Quaternions to represent rotation – http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation OBJ - http://en.wikipedia.org/wiki/Wavefront_.obj_file Alejandro Sans GDICG