Posts
Search
Contact
Cookies
About
RSS

The dreaded gimbal lock...

Added 31 Dec 2014, 8:01 p.m. edited 19 Jun 2023, 3:24 a.m.

noGimbal

Its common when using OpenGL to simply rotate objects by doing successive rotations around different axes, while keeping track of these rotations via 3 angles, one for each axis. This is simple to implement and if for example you're simply travelling over a plane or surface then you're unlikely to have much in the way of difficulty. However if your needs are more complex you will soon notice a number of problems. The first and possibly less noticeable is that doing successive rotations creates different results depending on the order you apply the rotations. Looking at the illustration above you will see that the three axes of a cube are marked with simple pyramids, when rotating around successive axes its often possible to get into a situation where the tip of the pyramid of a rotating axis is describing circles instead of being stationary as it should. By far the biggest issue is the so called gimbal lock, basically if you rotate one axis of rotation onto another axis, you'll loose a degree of freedom - its easier to see, just go onto any video sharing site and search for gimbal lock, its obvious whats going on as soon as you actually see whats going on... Many people will simply say the solutions is to use quaternions, and yes you can solve gimble lock by using quaternions but they are not some simple magic bullet - indeed if you (don't!) know what you're doing, you can implement all the same problems you're trying to solve! Its also possible to avoid gimbal lock by using a matrix too, and once you've seen how a quaternion is used you should be able to work out how to use a (rotation) matrix instead if you want to... The exact details of quaternions are complex (in fact more than complex ;) !) but similarly to matrices you really don't need to understand all the detailed minutiae to use them as a tool. The key here is not to use 3 Euler rotations to keep track of your objects rotation but rather a single quaternion to hold the orientation of your object. In particular you must add cumulative rotations (as you need them) to a quaternion rather than attempting to create a quaternion from Euler angles each time you need it. Lets look at some example code

            if (keys[GLFW_KEY_Q]) xa =- ANG_SPEED * delta;
            if (keys[GLFW_KEY_W]) xa =+ ANG_SPEED * delta;
            if (keys[GLFW_KEY_A]) ya =- ANG_SPEED * delta;
            if (keys[GLFW_KEY_S]) ya =+ ANG_SPEED * delta;
            if (keys[GLFW_KEY_Z]) za =- ANG_SPEED * delta;
            if (keys[GLFW_KEY_X]) za =+ ANG_SPEED * delta;

            model.translation( x, y, z );

            if (xa!=0) { tmpRot.axisAngle(1,0,0,xa); modelRot.multiply(tmpRot); }
            if (ya!=0) { tmpRot.axisAngle(0,1,0,ya); modelRot.multiply(tmpRot); }
            if (za!=0) { tmpRot.axisAngle(0,0,1,za); modelRot.multiply(tmpRot); }

            tmpMat.set(modelRot);
            model.multiply(tmpMat);

            mvp.set(vp);
            mvp.multiply(model);
You should always use a delta time component to achieve smooth animation, and here we can see that this means we'll need to create a temporary quaternion (tmpRot) to hold the amount of rotation we need to be added to the current orientation quaternion (modelRot). Aside from that once you have rotated your orientation you can apply it to your matrix calculations as you normally would. As usual I've attached a fully working example, as well as the gimbal code its also an example of using lwjgl3 with GL > version 3 (ie all modern like!) so theres some utilities and math routines you might find handy. Enjoy!