5

He pasado un par de semanas sobre este tema y parece que no puedo encontrar una solución adecuada y necesito algunos consejos.LWJGL - Problemas al implementar 'rollo' en una cámara 6DOF usando cuaterniones y una matriz de traducción

Estoy trabajando en la creación de una clase de cámara usando LWJGL/Java, y estoy usando Quaternions para manejar las rotaciones de rodamiento (cabeceo), cabeceo y balanceo. Me gustaría que esta cámara maneje los 6 grados de movimiento en el espacio tridimensional y ruede. Bearing, Pitch and Roll son cuaterniones. Los multiplico en un cuaternión de "cambio" y creo una matriz de traducción a partir de eso. Lo pongo en un buffer flotante, y multiplico la matriz modelview por mi buffer que contiene la matriz de rotación.

Puedo hacer que las rotaciones de rumbo y rodamiento funcionen correctamente, pero cuando implemento el rollo, me estoy encontrando con problemas. Principalmente, girar alrededor del eje Z (rodar) no parece funcionar. Siempre que "giro" la cámara, parece rodar alrededor del eje Z global en lugar del eje de dirección de la cámara local. Por lo general, puedo obtener 2 de los 3 para que funcionen dependiendo del orden en que multiplique los cuaterniones, pero no puedo hacer que funcionen juntos.

Dado que todas funcionan de forma independiente, supongo que hay un problema con mi método de orientación donde las combino y construyo una matriz de rotación. Tengo problemas para pegar toda la clase, así que aquí están los métodos y las declaraciones relativas a la rotación:

private final static float DEGTORAD = (float)(Math.PI/180);  

//Eye - position of the camera in the 3D world. 
private Vector3f eye; 

//Camera axis vectors, calculated each time reorient() is called. 
//Initialized to global x, y, and z axis initially. 
private Vector3f up; 
private Vector3f right; 
private Vector3f direction; 

//Angles of rotation (in degrees)  
private float pitchAngle; 
private float bearingAngle; 
private float rollAngle; 

private Quaternion pitch; 
private Quaternion bearing; 
private Quaternion roll; 

private FloatBuffer viewMatrixBuffer = BufferUtils.createFloatBuffer(16); 
private Quaternion currentOrientation; 

...

/** 
* Change the bearing (yaw) 
* @param bearing delta in degrees 
*/ 
public void bearing(float bearingDelta){ 
    bearingAngle += bearingDelta; 
    if(bearingAngle > 360){ 
     bearingAngle -= 360; 
    }else if(bearingAngle < 0){ 
     bearingAngle += 360; 
    } 
    bearing.setFromAxisAngle(new Vector4f(0f, 1f, 0f, bearingAngle * DEGTORAD)); 
    bearing.normalise(); 
} 

/** 
* Change the pitch 
* @param pitch delta in degrees 
*/ 
public void pitch(float pitchDelta){ 
    pitchAngle += pitchDelta; 
    if(pitchAngle > 360){ 
     pitchAngle -= 360; 
    }else if(pitchAngle < 0){ 
     pitchAngle += 360; 
    } 
    pitch.setFromAxisAngle(new Vector4f(1f, 0f, 0f, pitchAngle * DEGTORAD)); 
    pitch.normalise(); 
} 

/** 
* @param initialRoll 
*/ 
public void roll(float initialRoll) { 
    rollAngle += initialRoll; 
    if(rollAngle > 360){ 
     rollAngle -= 360; 
    }else if(rollAngle < 0){ 
     rollAngle += 360; 
    } 
    roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD)); 
    roll.normalise(); 
} 

/** 
* Change direction to focus on a certain point in the world 
* @param eye 
*/ 
public void lookThrough(){ 
    reorient(); 
    GL11.glMultMatrix(viewMatrixBuffer); 
}  

public void reorient(){ 
    //Multiply in order: bearing, pitch, roll. Non-commutative! 
    Quaternion change = new Quaternion(); 
    Quaternion.mul(bearing, pitch, change); 
    Quaternion.mul(roll, change, change); 
    // orient the camera... 
    Matrix4f rotationMatrix = getRotationMatrix(change); 

    //Get the looking direction 
    direction.x = rotationMatrix.m20; 
    direction.y = rotationMatrix.m21; 
    direction.z = rotationMatrix.m22; 

    //Set the position 
    rotationMatrix.m30 = eye.x; 
    rotationMatrix.m31 = eye.y; 
    rotationMatrix.m32 = eye.z; 
    rotationMatrix.m33 = 1; 

    rotationMatrix.invert(); 
    rotationMatrix.store(viewMatrixBuffer); 

    viewMatrixBuffer.rewind(); 

    Vector3f.cross(new Vector3f(0,1,0), direction, null).normalise(right); 
    Vector3f.cross(right, direction, null).normalise(up);    
} 

Vector3f, cuaternión, y Matrix4f son todas las clases lwjgl, no hecho a medida.

Así que mi pregunta es, dado 3 Quaternions que representan Teniendo, Pitch and Roll, ¿cómo modifico la matriz ModelView para representar con precisión estas rotaciones?

EDIT: Siento que esto está muy cerca. Ver el enlace Gist en el comentario de RiverC. Después de girar tantos grados, la vista salta mucho antes de volver a la normalidad al rodar. La esencia está allí, pero todavía está un poco apagado.

Respuesta

3

Estás haciendo las multiplicaciones en el orden incorrecto.

Para dos rotaciones q1 y q2, si q2 va a seguir q1 (dado que las rotaciones generalmente no son comunes) multiplique q2 * q1.

En un sistema de cardán, como los controles para un FPS, el orden de prioridad siempre es yaw, pitch, roll. Esto sugeriría el siguiente cálculo:

roll * pitch * yaw 

Como punto de java, sugeriría no crear nuevos cuaterniones para cada actualización, pero de todos modos

change = Quaternion.mul(Quaternion.mul(roll, pitch, change), yaw, change); 

Si nos fijamos en el código, el tercer Quaternion simplemente se se sobrescribirá con el resultado, por lo que no es necesario restablecerlo ni volver a crearlo en cada cuadro/actualización.

Esta orden de rotación es confusa, pero si mira la página Wiki en Quaternions, es la regla general.

+0

Gracias por la actualización. No he tocado este código por un tiempo, así que estoy volviendo a la velocidad con LWJGl. Creo que estoy cerca. Después de hacer los cambios y cambios sugeridos por gsimard, me estoy acercando. Sin embargo, cuando yaw (teniendo en la pregunta) está en 0, el tono está bien. Cuando la guiñada está en 180, el tono se invierte (el cabeceo hacia arriba mueve la cámara hacia abajo). Además, la rotación se vuelve esporádica después de girar unos 20 grados. Si sigo girando, se nivela. Entonces algo todavía no está del todo bien. Pondré el código en github y actualizaré esta pregunta más tarde esta noche. Creo que ya casi está allí. Gracias de nuevo. – framauro13

+0

¿Por qué haces cosas en la matriz de rotación después de crearla? Deberías poder enviar un Mat4 directamente al Shader y usarlo para multiplicar los vértices de la escena sin modificarlo. Lo que quiero decir es, la parte '// establecer la dirección' del código. Parece que estás tratando de usar eso para compensar la cámara a través de la multiplicación, pero en mi experiencia esto ha sido impredecible. Prueba a soltar eso. –

+0

I * think * la razón por la que lo hice inicialmente fue para poder sumar el vector de dirección con el vector de ojo para generar un vector para interactuar con objetos en el mundo. Un vector de 'recolección' más o menos. Además, he solucionado el problema con el lanzamiento de tono. Cambié el orden de rumbo y cabeceo, por lo que la línea dice 'Quaternion.mul (Quaternion.mul (rodar, llevar, cambiar), cabeceo, cambio);' Todavía estoy tratando de descubrir por qué el rollo se pone pesado después de rodar un cierto cantidad. – framauro13

3

Sé que esto es viejo, pero déjame adivinar de todos modos. El problema es exactamente lo que dijo usted mismo:

Cuando "giro" la cámara, parece que gira alrededor del eje Z global en lugar del eje de dirección de la cámara local.

Lo hace porque usted le pidió que rodara alrededor del vector (0,0,1), es decir, el eje Z global.

Esto es lo que hacen los cuaterniones de la unidad: giran un vector (aquí un conjunto de vectores, su matriz de rotación) alrededor de un eje especificado por su parte vectorial imaginaria (x, y, z) mediante alguna función angular del w escalar (w = cos (ángulo/2)).

Si entiendo lo que está tratando de hacer, que está rodando su cámara como al inclinar la cabeza de izquierda a derecha, a continuación, se debe construir un cuaternión rollo alrededor de su vector de dirección:

roll.setFromAxisAngle(new Vector4f(direction.x, direction.y, direction.z, rollAngle * DEGTORAD)); 

I' m suponiendo que su vector de dirección está normalizado, o que LWJGL sabe qué hacer con un vector de eje no unitario al llamar al setFromAxisAngle.

+0

Gracias por la actualización. Hice este cambio y está cerca, sin embargo, todavía tengo un comportamiento extraño. Hice este cambio con el sugerido por RiverC, y expliqué los nuevos problemas en un comentario allí. Voy a actualizar cuando llegue a casa esta noche con más detalles. Gracias de nuevo, agradezco la ayuda. – framauro13

Cuestiones relacionadas