我在 OpenGL 中的四元数旋转有什么问题?

What is wrong with my Quaternion rotation in OpenGL?

网上的笔记有助于正确构造四元数类。我的问题是将它们投入实际使用并在原始 OpenGL 中使用它们进行旋转。

在我的输入事件中,我有以下内容:

Quaternion<float> qPrev = qRot;
qRot = qRot.UnitQuaternion(); // #1.
Quaternion<float> axis(5., 0., 1., 0.);
qRot = qPrev*axis;
qRot *= qPrev.conjugate();
//#2. qRot = qRot.UnitQuaternion();

如果我使用#1 使旋转结果单元旋转几秒钟,加速然后完全消失。

如果我将#2 用于单元结果,则框 "wobbles" 并且永远不会旋转。

或者,我根据其他实现使用了它:

Quaternion<float> qPrev = qRot;
Quaternion<float> axis(-5./100, 0., 1., 0.); // #3.
axis = axis.UnitQuaternion();
qRot = qPrev*axis;
// #4. qRot *= qPrev.conjugate();

其中#3。最有意义的是,采用非单位 quat 的单位并将其最初与恒等式 quat 相乘;将所有内容保存在 unit-quats 中。

哪里#4。根据我对方程旋转定义的理解,尝试乘以共轭。

所有这些都会产生一个小的摆动,#1 是我得到的最接近的;盒子旋转,然后快速旋转,然后消失。


我的理解是我有一个轴 I 可以围绕 w = 多少(弧度角度)旋转。我简单地乘以方向并将结果乘以方向的负值;例如共轭。


渲染代码(这可能是罪魁祸首):

// Apply some transformations
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -100.f);
glRotatef(qRot.x, 1., 0, 0);
glRotatef(qRot.y, 0, 1., 0);
glRotatef(qRot.z, 0, 0, 1.);

// Draw the cube
glDrawArrays(GL_TRIANGLES, 0, 36);

// Draw some text on top of our OpenGL object
window.pushGLStates();

感谢您的帮助


编辑:非常感谢@ybungalobill 的帮助

我想要的四元数是四元数数学方面的一个非常具体的应用程序。我想要一个旋转轴,而在我的示例中,我正在创建一个具有直接值的四元数。这不是旋转矩阵的推导方式。我需要更多步骤:

void FromAxisAngle(_Tp theta, _Tp x, _Tp y, _Tp z)
{
    //Angle should be in radians
    this->w = cos(theta/2);

    //Axes
    this->x = x * sin(theta/2);
    this->y = y * sin(theta/2);
    this->z = z * sin(theta/2);
    //Normalize
    *this = this->UnitQuaternion();
}

同样,获取旋转矩阵是一个非常具体的应用程序,具有非常具体的步骤。四元数不这样做 'out of the box'.

旋转逻辑也有一些小的变化:

   Quaternion<float> qPrev = qRot;
   Quaternion<float> axis;
   axis.FromAxisAngle(-5./100, 1., 0., 0.);
   qRot = qPrev*axis;

所以现在我已经从我的旋转轴创建了一个数学上正确的四元数。然后相乘。

最后,我必须做的最后一件事是从我的四元数创建一个 OpenGL 可以使用的矩阵。所以我们有更多的数学要做,以便用旋转事物的符号来获得它:

void GetMatrix(_Tp matrix[16])
{
   Quaternion<_Tp> temp(this->UnitQuaternion());
  _Tp qw = temp.w;
  _Tp qx = temp.x;
  _Tp qy = temp.y;
  _Tp qz = temp.z;

  matrix[0] = 1.0f - 2.0f*qy*qy - 2.0f*qz*qz;
  matrix[1] = 2.0f*qx*qy - 2.0f*qz*qw;
  matrix[2] = 2.0f*qx*qz + 2.0f*qy*qw;
  matrix[3] = 0;

  matrix[4] = 2.0f*qx*qy + 2.0f*qz*qw;
  matrix[5] = 1.0f - 2.0f*qx*qx - 2.0f*qz*qz;
  matrix[6] = 2.0f*qy*qz - 2.0f*qx*qw;
  matrix[7] = 0;

  matrix[8] = 2.0f*qx*qz - 2.0f*qy*qw;
  matrix[9] = 2.0f*qy*qz + 2.0f*qx*qw;
  matrix[10] = 1.0f - 2.0f*qx*qx - 2.0f*qy*qy;
  matrix[11] = 0;

  matrix[12] = 0;
  matrix[13] = 0;
  matrix[14] = 0;
  matrix[15] = 1;
}

使用glMultMatrix立方体旋转。

glRotatef(qRot.x, 1., 0, 0);
glRotatef(qRot.y, 0, 1., 0);
glRotatef(qRot.z, 0, 0, 1.);

这不是将四元数旋转应用于 GL 状态的方式。您必须按照 here 中的公式将其转换为矩阵,然后调用 glMultMatrixglMultTransposeMatrix。在该方法之后,#3 将按预期工作。


将任何非零四元数转换为矩阵的代码,来自 Stannum 库:

template<class T>
mat<T,3,3> mat_rotation(const quat<T> &x)
{
    T s = 2/norm2(x); // cheap renormalization even of non-unit quaternions
    T wx = x.w*x.x, wy = x.w*x.y, wz = x.w*x.z;
    T xx = x.x*x.x, xy = x.x*x.y, xz = x.x*x.z;
    T yy = x.y*x.y, yz = x.y*x.z;
    T zz = x.z*x.z;

    return mat<T,3,3>(
        1 - s*(yy+zz),  s*(xy-wz),  s*(xz+wy),
        s*(xy+wz),  1 - s*(xx+zz),  s*(yz-wx),
        s*(xz-wy),  s*(yz+wx),  1 - s*(xx+yy)
    );
}