1
votes

Going off notes on the internet helps construct quaternion classes properly. My problem is putting them to practical use and using them for rotations in raw OpenGL.

In my input events I have the following:

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

If I use #1 make the rotation result unit it rotates fine for a few seconds, speeds up and then vanishes completely .

If I use #2 for a unit result the box "wobbles" and never rotates.

Alternatively, I've used this based off other implementations:

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

Where #3. makes the most sense, taking the unit of a non-unit quat and multiplying it initially with the identity quat; keeping everything in unit-quats.

Where #4. tried multiplying the conjugate based off my understanding of the equation's rotation definition.

All of these produce a small wobble and #1 is the closest I got; the box rotates, then quickly, then vanishes.


My understanding is that I have an axis I which to rotate around where w = how much (angle in radians). I simply multiply by the orientation and multiply that result by the negative of the orientation; e.g. the conjugate.


Rendering code (this could be the culprit):

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

I appreciate the help


Edit: Many thanks to @ybungalobill for helping out.

What I wanted from my quaternion is a very specific application in terms of quaternion math. I wanted an axis of rotation while in my example I'm creating a quaternion with the direct values. This is not how the rotation matrix is derived. I needed more steps:

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

Again, getting the rotation matrix is a very specific application with very specific steps. Quaternions don't do this 'out of the box'.

Some minor changes to the rotation logic as well:

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

So now I've created a mathematically correct quaternion FROM my rotation axis. Then multiply.

Finally, the last thing I had to do was create a matrix from my quaternion that OpenGL could use. So we have more math to do in order to get it in a notation that rotates things:

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

Using glMultMatrix the cube rotates.

1

1 Answers

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

That's not how you apply a quaternion rotation to the GL state. You have to convert it to a matrix following the formula from here and then call glMultMatrix or glMultTransposeMatrix. After that approach number #3 will work as expected.


Code that converts any non-zero quaternion to a matrix, from Stannum libs:

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