13
\$\begingroup\$

I am reading the online "Learning Modern 3D Graphics Programming" book by Jason L. McKesson

As of now, I am up to the gimbal lock problem and how to solve it using quaternions.

However right here, at the Quaternions page.

Part of the problem is that we are trying to store an orientation as a series of 3 accumulated axial rotations. Orientations are orientations, not rotations. And orientations are certainly not a series of rotations. So we need to treat the orientation of the ship as an orientation, as a specific quantity.

I guess this is the first spot I start to get confused, the reason is because I don't see the dramatic difference between orientations and rotations. I also don't understand why an orientation cannot be represented by a series of rotations...

Also:

The first thought towards this end would be to keep the orientation as a matrix. When the time comes to modify the orientation, we simply apply a transformation to this matrix, storing the result as the new current orientation.

This means that every yaw, pitch, and roll applied to the current orientation will be relative to that current orientation. Which is precisely what we need. If the user applies a positive yaw, you want that yaw to rotate them relative to where they are current pointing, not relative to some fixed coordinate system.

The concept, I understand, however I don't understand how if accumulating matrix transformations is a solution to this problem, how the code given in the previous page isn't just that.

Here's the code:

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack currMatrix;
    currMatrix.Translate(glm::vec3(0.0f, 0.0f, -200.0f));
    currMatrix.RotateX(g_angles.fAngleX);
    DrawGimbal(currMatrix, GIMBAL_X_AXIS, glm::vec4(0.4f, 0.4f, 1.0f, 1.0f));
    currMatrix.RotateY(g_angles.fAngleY);
    DrawGimbal(currMatrix, GIMBAL_Y_AXIS, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
    currMatrix.RotateZ(g_angles.fAngleZ);
    DrawGimbal(currMatrix, GIMBAL_Z_AXIS, glm::vec4(1.0f, 0.3f, 0.3f, 1.0f));

    glUseProgram(theProgram);
    currMatrix.Scale(3.0, 3.0, 3.0);
    currMatrix.RotateX(-90);
    //Set the base color for this object.
    glUniform4f(baseColorUnif, 1.0, 1.0, 1.0, 1.0);
    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));

    g_pObject->Render("tint");

    glUseProgram(0);

    glutSwapBuffers();
}

To my understanding, isn't what he is doing (modifying a matrix on a stack) considered accumulating matrices, since the author combined all the individual rotation transformations into one matrix which is being stored on the top of the stack.

My understanding of a matrix is that they are used to take a point which is relative to an origin (let's say... the model), and make it relative to another origin (the camera). I'm pretty sure this is a safe definition, however I feel like there is something missing which is blocking me from understanding this gimbal lock problem.

One thing that doesn't make sense to me is: If a matrix determines the difference relative between two "spaces," how come a rotation around the Y axis for, let's say, roll, doesn't put the point in "roll space" which can then be transformed once again in relation to this roll... In other words shouldn't any further transformations to this point be in relation to this new "roll space" and therefore not have the rotation be relative to the previous "model space" which is causing the gimbal lock.

That's why gimbal lock occurs right? It's because we are rotating the object around set X, Y, and Z axes rather than rotating the object around it's own, relative axes. Or am I wrong?

Since apparently this code I linked in isn't an accumulation of matrix transformations can you please give an example of a solution using this method.

So in summary:

  • What is the difference between a rotation and an orientation?
  • Why is the code linked in not an example of accumulation of matrix transformations?
  • What is the real, specific purpose of a matrix, if I had it wrong?
  • How could a solution to the gimbal lock problem be implemented using accumulation of matrix transformations?
  • Also, as a bonus: Why are the transformations after the rotation still relative to "model space?"
  • Another bonus: Am I wrong in the assumption that after a transformation, further transformations will occur relative to the current?

Also, if it wasn't implied, I am using OpenGL, GLSL, C++, and GLM, so examples and explanations in terms of these are greatly appreciated, if not necessary.

The more the detail the better!

Thanks in advance.

\$\endgroup\$

2 Answers 2

12
\$\begingroup\$

I'm not sure of a good way to preface this, other than I hope it ties together nicely by the end. That said, let's dive in:

A rotation and an orientation are different because the former describes a transformation, and the latter describes a state. A rotation is how an object gets into an orientation, and an orientation is the local rotated space of the object. This can be directly related to how the two are represented mathematically: a matrix stores transformations from one coordinate space to another (you did have that correct), and a quaternion directly describes an orientation. The matrix, therefore, can only describe how the object gets into an orientation, through a series of rotations. The problem with this, though, is Gimbal Lock.

Gimbal lock demonstrates the difficulty of getting an object into an orientation using a series of rotations. The problem occurs when at least two of the rotation axes align:

Image courtesy of deepmesh3d.com
In the left image above, the blue and orange axes make the same rotation! This is a problem, because this means one of the three degrees of freedom has been lost, and additional rotations from this point may produce unexpected results. Using quaternions solves this because apply a quaternion to transform the orientation of an object will directly put the object in a new orientation (that's the best way I can say it), rather than breaking the transformation down into roll, pitch, and yaw operations.

Now, I am actually skeptical about accumulating matrices being a complete solution to this, because accumlating matrices (therefore accumulating rotations) are exactly what can cause the Gimbal Lock problem in the first place. The proper way to handle transformation by a quaternion is to either perform quaternion multiplcation on a point:

pTransformed = q * pAsQuaternion * qConjugate

or by converting the quaternion to a matrix and transforming the point using that matrix.

A plain matrix rotation (such as a 45 degree yaw) will always be defined in global space. If you want to apply the transformation in local space, you would have to transform your transformation into that objects local space. It sounds strange, so I will elaborate. This is where the importance of the order of rotations comes in. I recommend grabbing a book here so that you can follow along with the transformations.

Start with the book flat, its cover facing up at the ceiling, oriented as if you were about to open it and start reading. Now tilt the front of the book up 45 degrees (the front cover should roughly be facing you):

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(45);

Now, let's say you wanted to adjust the yaw of the book 45 degrees (I think I'm assuming a right-handed coordinate system, so this will be changing heading to the left), and you want this to apply to the book's local coordinate space, so that the cover of the book will still face you:

bookMatrix.RotateY(45);

The problem is, this rotation occurs in the global coordinate space, so the book's cover will end facing over your right shoulder. In order to have this change in heading occur in local coordinate space, you should have applied it first!

glutil::MatrixStack bookMatrix;
bookMatrix.RotateY(45);
bookMatrix.RotateX(45);

Try it out! Start the book facing up at the ceiling again. Change its yaw 45 degrees, and then pitch it 45 degrees along the global X axis (running from your left to right). This the orientation you expected with a pitch of 45 and yaw of 45 in the book's local space.

What does this mean? All it really comes down to is that the order of the operations matters. Transformations done first become local transformations in the context of transformations done afterwards. It becomes a lot to wrap your head around, and this is how quaternions save a lot of trouble. The skip all the order-dependent stuff.

The other huge advantage that quaternions provide is that they allow for the interpolation of orientations. Trying to interpolate between Euler angles is almost impossible because of the order dependencies. The mathematical properties of the quaternion allow for a well-defined spherical linear interpolation between them.

To wrap things up and address your original question: accumulative matrix transformations really will not solve the Gimbal lock problem, unless the transformations are carefully chosen and applied in a precise order. Therefore, always use quaternions, and apply quaternions to points using quaternion multiplication.

Hope this helps :)

\$\endgroup\$
3
  • 5
    \$\begingroup\$ just for the record, quaternions can still introduce gimbal lock if described via Euler angles; as you will be doing the same calculation in a different way (quaternions rather than matrices) \$\endgroup\$
    – concept3d
    Apr 2, 2013 at 5:26
  • 1
    \$\begingroup\$ @concept3d - congrats for mentioning this! It's important to understand what makes the gimbal mechanism prone to losing a degree of freedom: it's like a robotic joint inherently describing an overdetermined system of equations. If you build this mechanism with quaternions, matrices or magic, you still end up with ambiguities - it's understanding it and not using it in the first place that's a real solution (unless you're required to use it for some demonstrative or technical purpose). \$\endgroup\$
    – teodron
    Apr 2, 2013 at 8:10
  • \$\begingroup\$ quaternions are hard to imagine, the way I always think about it is that they(unit quaternions) represent a 3-Sphere space, hence can represent any orientation, while I understand Euler angles each represent circles/turos, hence not a complete sphere this is not very accurate way to represent orientation (3 circles/torus cannot really generate every possible orientation unless they rotate independently which isn't possible in the case of euler angles), not sure if I explained accurately :) \$\endgroup\$
    – concept3d
    Apr 2, 2013 at 8:22
1
\$\begingroup\$

Matrix accumulations can in fact solve Gimbal lock. By accumulating rotations, you are adding gimbals, allowing any arbitrary rotation. The diagram that ktodisco provided shows a gimbal lock in the left diagram. The matrix for this orientation can be defined as:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);

Because of the y gimbal rotation, X and Z gimbals are now locked, so we've lost one degree of movement. At this point we have no yawing (local y, global z) using these three gimbals. But by adding another gimbal, I can rotate locally around the y:

glutil::MatrixStack bookMatrix;
bookMatrix.RotateX(90);
bookMatrix.RotateY(90);
bookMatrix.RotateZ(90);
bookMatrix.RotateY(90);

For every new roll, pitch, and yaw, just add another gimbal, ACCUMULATING them into one matrix. So every time another local rotation is needed, a rotation is created and multiplied to the accumulation matrix. As the chapter mentions, there are still problems, but gimbal lock is not one of them.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .