使用四元数的OpenGL轮作 [英] Using Quaternions for OpenGL Rotations

查看:179
本文介绍了使用四元数的OpenGL轮作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在写一个程序,对象走动spacesim风格,以学习如何顺利地通过三维空间中移动的东西。经过与欧拉乱搞角几分,看来他们是不是真的适合任意方向自由形式的3D动作,所以我决定来说明是什么似乎是最适合的工作 - 四元数。我打算为对象围绕其本地XYZ轴旋转,在任何时候,从来没有围绕全球XYZ轴。

So I'm writing a program where objects move around spacesim-style, in order to learn how to move things smoothly through 3D space. After messing around with Euler angles a bit, it seems they aren't really appropriate for free-form 3D movement in arbitrary directions, so I decided to move on to what seems to be best for the job - quaternions. I intend for the object to rotate around its local X-Y-Z axes at all times, never around the global X-Y-Z axes.

我试图实现旋转使用四元体系,但有什么不工作。当旋转沿单一轴的对象,如果没有previous旋转开展了,东西旋转细沿着一个给定的轴线。但是,在应用之后另一个已进行一次旋转时,第二旋转不总是沿局部轴线它应该被旋转沿 - 例如,约90°绕Z轴的旋转后,围绕Y轴的旋转还是发生围绕全局Y轴,而不是它与全局X轴对齐新的局部Y轴。

I've tried to implement a system of rotation using quaternions, but something isn't working. When rotating the object along a single axis, if no previous rotations were undertaken, the thing rotates fine along a given axis. However, when applying one rotation after another has been performed, the second rotation is not always along the local axis it's supposed to be rotating along - for instance, after a rotation of about 90° around the Z axis, a rotation around the Y axis still takes place around the global Y axis, rather than the new local Y axis which is aligned with the global X axis.

呵呵。所以,让我们通过这个一步一步来。这个错误必须在这里的某个地方。

Huh. So let's go through this step by step. The mistake must be in here somewhere.

第1步 - 捕捉输入

我想这将是最好使用欧拉角(或变桨偏航,滚动计划)捕捉玩家的输入。目前,方向键控制俯仰和偏航,而Q和E控制辊。我捕捉玩家输入,因此,(我用SFML 1.6):

I figured it would be best to use Euler angles (or a Pitch-Yaw-Roll scheme) for capturing player input. At the moment, arrow keys control Pitch and Yaw, whereas Q and E control Roll. I capture player input thus (I am using 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的事情我想旋转。它还包含了四元数和旋转矩阵重新presenting对象的旋转。

m_Entity is the thing I'm trying to rotate. It also contains the quaternion and rotation matrices representing the object's rotation.

第2步 - 更新四元数

我不是100%肯定这是它应该做的方式,但是这是我想在做实体:: ApplyForce():

I'm not 100% sure this is the way it's supposed to be done, but this is what I tried doing in Entity::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);

正如你所看到的,我不知道它是否是最好的,只是从更新的欧拉角建立一个新的四元数,还是我应该乘以四元数再presenting与四元数重新$ P的变化$ psenting整体当前旋转,这是IM pression我读本指南时得到的。如果是后者,我的code是这样的:

As you can see, I'm not sure whether it's best to just build a new quaternion from updated Euler angles, or whether I'm supposed to multiply the quaternion representing the change with the quaternion representing the overall current rotation, which is the impression I got when reading this guide. If the latter, my code would look like this:

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

m_Rotation是存储在PYR格式对象的当前旋转;旋转是由玩家输入所要求的变化。无论哪种方式,虽然,这个问题可能是我实现我的四元数类。这里是整个事情:

m_Rotation is the object's current rotation stored in PYR format; Rotation is the change demanded by player input. Either way, though, the problem might be in my implementation of my Quaternion class. Here is the whole thing:

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;
}

有可能是东西在里面,使别人更聪明比我畏缩,但我无法看到它。从欧拉角转换为四元数,我用了第一种方法,根据<一href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">this来源,这也似乎表明,该公式自动创建一个四元数(明确归一化)。对于乘以四元,我又画了这个C ++指南

There's probably something in there to make somebody wiser than me cringe, but I can't see it. For converting from Euler angles to a quaternion, I used the "first method" according to this source, which also seems to suggest that the equation automatically creates a unit quaternion ("clearly normalized"). For multiplying quaternions, I again drew on this C++ guide.

第3步 - 从四元数得出一个旋转矩阵

一旦做到这一点,按R. Martinho费尔南德斯'答案这个问题,我尝试建立从四元数的旋转矩阵,并用它来更新我的对象的旋转,使用上述四元数:: RotationMatrix()code以下行:

Once that is done, as per R. Martinho Fernandes' answer to this question, I try to build a rotation matrix from the quaternion and use that to update my object's rotation, using the above Quaternion::RotationMatrix() code in the following line:

m_qRotation.RotationMatrix(m_RotationMatrix);

我要指出,m_RotationMatrix是 GLfloat m_RotationMatrix [16] ,按的的/xhtml/glMultMatrix.xml">the所需的参数,我相信我应该显示的对象时,在以后的使用。它被初始化为:

I should note that m_RotationMatrix is GLfloat m_RotationMatrix[16], as per the required parameters of glMultMatrix, which I believe I am supposed to use later on when displaying the object. It is initialized as:

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

我认为是中性的OpenGL旋转矩阵(每4个值一起重新present列,正确的?再次,我得到这个从的的glMultMatrix 页)。

第4步 - 显示

最后,我们得到运行每个周期为对象的功能是应该显示它。

Finally, we get to the function run each cycle for the object that is supposed to display it.

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();

我已经离开我的previous失败的尝试,有评论说出来。

I have left my previous failed attempts there, commented out.

结论 - 悲伤的熊猫

这是玩家输入的生命周期结束时,从摇篮到OpenGL的管理严重。

That is the conclusion of the life cycle of player input, from cradle to OpenGL-managed grave.

我显然不明白的东西,因为我得到的行为是不是我想要或期望的行为。但我不是特别有经验的矩阵数学或四元数,所以我没有看到我的方式错误所需要的洞察力。

I've obviously not understood something, since the behavior I get isn't the behavior I want or expect. But I'm not particularly experienced with matrix math or quaternions, so I don't have the insight required to see the error in my ways.

有人可以帮助我在这里?

Can somebody help me out here?

推荐答案

所有你所做的是切实落实欧拉角与四元数。这是没有帮助。

All you have done is effectively implement Euler angles with quaternions. That's not helping.

欧拉角的问题是,当你计算的矩阵,每个角度相对于它之前的矩阵的旋转。你需要的是采取一种对象的当前的取向的,并沿着某个坐标轴应用旋转,产生一个新的方向。

The problem with Euler angles is that, when you compute the matrices, each angle is relative to the rotation of the matrix that came before it. What you want is to take an object's current orientation, and apply a rotation along some axis, producing a new orientation.

您不能做到这一点与欧拉角。您可以使用矩阵,你还可以用四元数(因为他们是矩阵的只是转动部分)。但是你不能用pretending他们是欧拉角做到这一点。

You can't do that with Euler angles. You can with matrices, and you can with quaternions (as they're just the rotation part of a matrix). But you can't do it by pretending they are Euler angles.

这是不是在存储的角度做的所有的。相反,你只是有一个四元数从而重新presents对象的当前方向。 (某些角度有些轴),当你决定要一个旋转适用于它,你构建一个重新presents的旋转通过围绕该轴的角度的四元数。然后你右键乘以四元数与当前的定向四元数,产生一个新的电流方向。

This is done by not storing angles at all. Instead, you just have a quaternion which represents the current orientation of the object. When you decide to apply a rotation to it (of some angle by some axis), you construct a quaternion that represents that rotation by an angle around that axis. Then you right-multiply that quaternion with the current orientation quaternion, producing a new current orientation.

在渲染对象,可以使用当前的方向是...的方向。

When you render the object, you use the current orientation as... the orientation.

这篇关于使用四元数的OpenGL轮作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆