Thursday, March 13, 2014

The Perils of Euler Angles

If you've ever used a 3D program before, you've probably encountered Euler angles. You know, they're that thing where you have a rotation for the X, Y, and Z axis. Blender illustrates this nicely with it's rotate tool.

Euler angles make it nice and easy to orient objects in 3D space. If you've done any 3D modeling before, it's hard to imagine orienting things without them.

I'm okay with that. That's not the part that scares me.

What scares me is when I see programmers using Euler angles. Why? If you've written any serious rotation code, you'll know that Euler angles are an absolute programming nightmare.

Allow me to demonstrate. Let's say you want to interpolate between these two rotations.


That's easy! Just interpolate the Z angle and you've got a nice, smooth rotation!
Not so fast! What if you aren't rotating along a global axis?


As soon as you stop playing nice and rotate around a non-global axis, proper interpolation becomes nearly impossible. The fundamental problem here is that Euler angles have no notion of an axis of rotation. They store an orientation and that's it.

But wait, it gets worse. Let's say you do some heroic programming and derive a smooth rotation scheme with Euler angles. Well done! Unfortunately, there's a darker force lurking in the shadows.

You may have noticed in the images that the rotation tool got lopsided with certain rotations. That's because once an object rotates around one axis, the other axes rotate with it. That's all fine except for one little thing: What happens when a rotation leaves two of the axes aligned?

Well...
At this point, rotations around the X and Z axis are equivalent. You want to rotate around that missing axis? Too bad! You've encountered the lovely phenomenon of gimbal lock.

At best, this can cause your object to suddenly flip around 180 degrees. At worst, it can cause your rotation code to fail entirely. There's some heroics you can do to get around this, such as keeping a hidden fourth rotation axis, but at this point the trend should be clear. Euler angles are fundamentally the wrong approach to storing rotations.

So how should you store rotations then? That ultimately depends on what you're trying to do, but as a general replacement might I suggest (brace yourself) quaternions? Don't listen to the 4D hypersphere naysayers, quaternions are easy!

By their very nature, quaternions solve a lot of the issues with Euler angles. Need to know the axis of rotation? Well guess what? A quaternion's X/Y/Z is the axis of rotation. No need for crazy interpolation schemes either because your basic linear interpolation (plus normalization for good measure!) performs a nice interpolation by itself.

Perhaps best of all, it's ridiculously simple to create a quaternion from an axis and angle, making the entire non-global rotation axis dilemma moot. 

Of course, quaternions might not be the right solution depending on your needs, but almost anything is better Euler angles. Let your users orient objects with Euler angles all they want; after all it's a simple and intuitive way to select an orientation. Internally, however, make sure your code is using a different rotation structure unless you want to suffer the perils of Euler angles.

5 comments:

  1. Hello thebennybox! I'm having a bit of a problem. I followed your "Intro to OpenGL" windows setup, but I keep getting "error LNK1561: entry point must be defined". Any ideas? Code :
    #include
    #include
    #include

    int main(int argc, char** argv)
    {

    return 0;
    }

    ReplyDelete
    Replies
    1. Erm... I've got one question. Why do you have 3 include statements that have nothing actually specified to be included?

      Delete
    2. That's exactly what I was thinking.

      Delete
  2. Simply use this code:
    #include
    using namespace std;

    int main()
    {
    cout<< "Hello World";
    return 0;
    }

    Alternatively if you are using a bunch of libraries you could do this:

    #include


    int main()
    {
    std::cout<< "Hello World";
    return 0;
    }

    -Uladox

    ReplyDelete
  3. Wait... what happened to my include?
    Google blogger error with the less than or greater than sign?
    After include there should be a less than sign then the word iostream then greater than sign.

    ReplyDelete