使用四元数的OpenGL轮作四元数、OpenGL

2023-09-07 17:46:27 作者:休書づ絕愛

所以我在写一个程序,对象走动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;
}

有可能是东西在里面,使别人更聪明比我畏缩,但我无法看到它。从欧拉角转换为四元数,我用了第一种方法,根据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] ,按的