Utilisation de quaternions pour les rotations OpenGL

Donc je suis en train d'écrire un programme où les objets se déplacent autour de spacesim de style, afin d'apprendre comment faire bouger les choses en douceur dans l'espace 3D. Après déconner avec les angles d'Euler un peu, il semble qu'ils ne sont pas vraiment approprié pour la forme libre de mouvement en 3D dans les directions arbitraires, j'ai donc décidé de passer à ce qui semble être le mieux pour l'emploi des quaternions. J'ai l'intention de l'objet à faire pivoter autour de son local X-Y-Z axes en tout temps, jamais autour de la global X-Y-Z axes.

J'ai essayé de mettre en place un système de rotation à l'aide des quaternions, mais quelque chose ne fonctionne pas. Lors de la rotation de l'objet le long d'un seul axe, si pas de rotations précédentes ont été menées, la chose tourne fine le long d'un axe. Cependant, lors de l'application d'une rotation après l'autre a été réalisée, la deuxième rotation n'est pas toujours le long de l'axe local, c'est censé être de tourner - par exemple, après une rotation d'environ 90° autour de l'axe Z, une rotation autour de l'axe Y, a toujours lieu autour du mondial de l'axe Y, plutôt que de les nouveaux locaux de l'axe Y qui est aligné avec le mondial de l'axe X.

Hein. Donc, nous allons passer par cette étape par étape. L'erreur doit être ici quelque part.

ÉTAPE 1 - Capture d'Entrée

J'ai pensé qu'il serait préférable d'utiliser des angles d'Euler (ou un Tangage-Lacet-Roulis schéma) pour la capture de l'entrée du lecteur. Pour le moment, les touches fléchées de contrôle de Tangage et de Lacet, alors que Q et E contrôle de Roulis. J'ai fait une capture d'entrée du lecteur ainsi (je suis en utilisant la SFML 1.6):

    ///SPEEDS
    float ForwardSpeed = 0.05;
    float TurnSpeed = 0.5;

    //Rotation
    sf::Vector3<float> Rotation;
    Rotation.x = 0;
    Rotation.y = 0;
    Rotation.z = 0;
    //PITCH
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Up) == true)
    {
        Rotation.x -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Down) == true)
    {
        Rotation.x += TurnSpeed;
    }
    //YAW
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Left) == true)
    {
        Rotation.y -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Right) == true)
    {
        Rotation.y += TurnSpeed;
    }
    //ROLL
    if (m_pApp->GetInput().IsKeyDown(sf::Key::Q) == true)
    {
        Rotation.z -= TurnSpeed;
    }
    if (m_pApp->GetInput().IsKeyDown(sf::Key::E) == true)
    {
        Rotation.z += TurnSpeed;
    }

    //Translation
    sf::Vector3<float> Translation;
    Translation.x = 0;
    Translation.y = 0;
    Translation.z = 0;

    //Move the entity
    if (Rotation.x != 0 ||
        Rotation.y != 0 ||
        Rotation.z != 0)
    {
        m_Entity->ApplyForce(Translation, Rotation);
    }

m_Entity est la chose que je suis en train de tourner. Il contient également le quaternion de rotation et de matrices représentant la rotation de l'objet.

ÉTAPE 2 - mise à Jour de quaternions

Je ne suis pas 100% sûr que c'est la façon dont il est censé être fait, mais c'est ce que j'ai essayé de faire au sein de l'Entité::ApplyForce():

//Rotation
m_Rotation.x += Rotation.x;
m_Rotation.y += Rotation.y;
m_Rotation.z += Rotation.z;

//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(m_Rotation.x, m_Rotation.y, m_Rotation.z);//* m_qRotation;

m_qRotation.RotationMatrix(m_RotationMatrix);

Comme vous pouvez le voir, je ne suis pas sûr de savoir si il est préférable de construire une nouvelle quaternion de mise à jour angles d'Euler, ou si je suis censé multiplier le quaternion représentant la variation avec le quaternion représentant l'ensemble de la rotation actuelle, qui est l'impression que j'ai eue lors de la lecture de ce guide. Si ce dernier, mon code ressemble à ceci:

//Multiply the new Quaternion by the current one.
m_qRotation = Quaternion(Rotation.x, Rotation.y, Rotation.z) * m_qRotation;

m_Rotation est l'objet de la rotation actuelle stockées dans les PYR format; la Rotation est le changement demandé par le joueur d'entrée. De toute façon, même si, le problème est peut-être dans ma mise en œuvre de mon Quaternion classe. Voici la chose:

Quaternion::Quaternion(float Pitch, float Yaw, float Roll)
{
float Pi = 4 * atan(1);
//Set the values, which came in degrees, to radians for C++ trig functions
float rYaw = Yaw * Pi / 180;
float rPitch = Pitch * Pi / 180;
float rRoll = Roll * Pi / 180;
//Components
float C1 = cos(rYaw / 2);
float C2 = cos(rPitch / 2);
float C3 = cos(rRoll / 2);
float S1 = sin(rYaw / 2);
float S2 = sin(rPitch / 2);
float S3 = sin(rRoll / 2);
//Create the final values
a = ((C1 * C2 * C3) - (S1 * S2 * S3));
x = (S1 * S2 * C3) + (C1 * C2 * S3);
y = (S1 * C2 * C3) + (C1 * S2 * S3);
z = (C1 * S2 * C3) - (S1 * C2 * S3);
}
//Overload the multiplier operator
Quaternion Quaternion::operator* (Quaternion OtherQuat)
{
float A = (OtherQuat.a * a) - (OtherQuat.x * x) - (OtherQuat.y * y) - (OtherQuat.z * z);
float X = (OtherQuat.a * x) + (OtherQuat.x * a) + (OtherQuat.y * z) - (OtherQuat.z * y);
float Y = (OtherQuat.a * y) - (OtherQuat.x * z) - (OtherQuat.y * a) - (OtherQuat.z * x);
float Z = (OtherQuat.a * z) - (OtherQuat.x * y) - (OtherQuat.y * x) - (OtherQuat.z * a);
Quaternion NewQuat = Quaternion(0, 0, 0);
NewQuat.a = A;
NewQuat.x = X;
NewQuat.y = Y;
NewQuat.z = Z;
return NewQuat;
}
//Calculates a rotation matrix and fills Matrix with it
void Quaternion::RotationMatrix(GLfloat* Matrix)
{
//Column 1
Matrix[0] = (a*a) + (x*x) - (y*y) - (z*z);
Matrix[1] = (2*x*y) + (2*a*z);
Matrix[2] = (2*x*z) - (2*a*y);
Matrix[3] = 0;
//Column 2
Matrix[4] = (2*x*y) - (2*a*z);
Matrix[5] = (a*a) - (x*x) + (y*y) - (z*z);
Matrix[6] = (2*y*z) + (2*a*x);
Matrix[7] = 0;
//Column 3
Matrix[8] = (2*x*z) + (2*a*y);
Matrix[9] = (2*y*z) - (2*a*x);
Matrix[10] = (a*a) - (x*x) - (y*y) + (z*z);
Matrix[11] = 0;
//Column 4
Matrix[12] = 0;
Matrix[13] = 0;
Matrix[14] = 0;
Matrix[15] = 1;
}

Il y a probablement quelque chose à y faire à quelqu'un de plus sage que moi grincer des dents, mais je ne peux pas le voir. Pour convertir les angles d'Euler pour un quaternion, j'ai utilisé la première méthode", selon cette sourcequi semble également suggérer que l'équation crée automatiquement une unité de quaternions ("clairement normalisée"). Pour la multiplication des quaternions, j'ai de nouveau attiré sur ce C++ guide.

ÉTAPE 3 - calcul de la matrice de rotation dans le quaternion

Une fois que c'est fait, comme par R. Martinho Fernandes réponse à cette questionj'essaie de construire une matrice de rotation dans le quaternion et de l'utiliser pour mettre à jour mon objet en rotation, en utilisant les Quaternions::RotationMatrix() code dans la ligne suivante:

m_qRotation.RotationMatrix(m_RotationMatrix);

Je note que m_RotationMatrix est GLfloat m_RotationMatrix[16]comme par les paramètres requis de glMultMatrixje crois que je suis censé l'utiliser plus tard lors de l'affichage de l'objet. Elle est initialisée comme:

m_RotationMatrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};

Je crois que c'est le "neutre" OpenGL matrice de rotation (tous les 4 valeurs représentent ensemble une colonne, correct? Encore une fois, je obtenir ce à partir de le glMultMatrix page).

ÉTAPE 4 - Afficher!

Enfin, nous arrivons à la fonction d'exécution de chaque cycle pour l'objet qui est censé afficher.

glPushMatrix();
glTranslatef(m_Position.x, m_Position.y, m_Position.z);
glMultMatrixf(m_RotationMatrix);
//glRotatef(m_Rotation.y, 0.0, 1.0, 0.0);
//glRotatef(m_Rotation.z, 0.0, 0.0, 1.0);
//glRotatef(m_Rotation.x, 1.0, 0.0, 0.0);
//glRotatef(m_qRotation.a, m_qRotation.x, m_qRotation.y, m_qRotation.z);
//[...] various code displaying the object's VBO
glPopMatrix();

J'ai laissé mes précédentes tentatives infructueuses là, commenté.

Conclusion - Sad panda

Qui est la conclusion du cycle de vie du joueur, du berceau à OpenGL-géré grave.

Je n'ai évidemment pas compris quelque chose, puisque le comportement je reçois n'est pas le comportement que je veux ou attendre. Mais je ne suis pas particulièrement connu avec la matrice de mathématiques ou de quaternions, donc je n'ai pas le savoir-faire nécessaire pour voir l'erreur dans mes moyens.

Quelqu'un peut-il m'aider ici?

source d'informationauteur GarrickW