142. 3D Rotations (flying a plane)
Until now, I haven’t had to do complex rotations in 3D, because I’ve mostly worked on a table top where the only rotation is left and right.
Now, however, I have an aeroplane that rotates in all three dimensions, and I cannot avoid it any longer. I have to master a fearsome pair of concepts
- gimbal lock
- quaternions
And if you have any interest in 3D modelling, this is something you will need to learn, too. But as always, I will try to keep it simple.
Euler* angles
*did you know Euler is pronounced “oiler” not “you-ler”?
If you’ve done any 3D modelling, you will know that if an aeroplane faces forward at (0,0,0), then
- the x axis goes through the wings (from negative in the left wing, to positive in the right wing)
- the y axis goes straight up from the wheels to the top of the fuselage
- the z axis goes from negative at the nose, to positive at the tail
So if you rotate the plane on
- the x axis, it climbs up or dives down (=”pitch”)
- the y axis, it turns left or right (=”yaw”)
- the z axis, it tilts to the left or right (=”roll”)
The natural way to rotate in 3D is to turn one or more of the x, y and z axes until you are facing in the direction you want. But you can only turn on one axis at a time, so if you need to turn left and also look up, then you can either
- turn left and then turn upwards, or else
- turn upwards and then turn left.
So you turn one axis at a time, and you can choose what order to do this in, eg X, Y, Z – or Z, Y , X – or any of the 6 possible sequences.
There is a Codea command for rotation – you give it the number of degrees to rotate, and tell it which axis to rotate, as in the example below.
rotate( 30, 0, 1, 0 ) --rotate y axis by 30 degrees rotate( -45, 1, 0, 0 ) --rotate x axis by -45 degrees rotate( 10, 0, 0, 1 ) --rotate z axis by 10 degrees
This approach is known as using Euler angles.
And it has some problems.
First, if you are in a plane that is halfway through a loop, hanging upside down at a strange angle in the sky, how do you figure out where the plane will be next time you draw it? You have to somehow
- calculate any new rotations since you last drew the plane, then
- add them to the existing rotation angles
Another problem is that rotating (say) X, Y then Z may not give you the same result as (say) Z, X then Y. Changing the order of rotation can give you completely different results.
This is bad enough, but there is something worse, called gimbal lock. While I understand its effect, it is difficult to visualise, which makes it tricky for me to describe it to you. So I’m going to give a very brief explanation, and if you want it understand it better, go to YouTube and watch a couple of videos about it.
Gimbal lock
Gimbals generally consist of three rings set inside each other, as shown below. By turning the rings, you can point a vehicle in any direction.
Gimbal lock happens when you turn one of the axes by 90 degrees and it lines up exactly with one of the other axes, preventing it from moving properly. This causes dangerous situations in real life flight systems (even the Apollo astronauts had to be careful to avoid it).
Gimbal lock also occurs in computer graphics, but it doesn’t prevent any of the axes from moving properly, because they aren’t physically blocking each other. The problem is a different one.
The best illustration I’ve read is this.
- Suppose you are flying a plane to the North
- you are flying level, and your x, y and z rotations are all zero
- you loop the loop, as shown below, starting from “Bottom”, through “A” and “Top”, and down again
- As you climb upwards toward the vertical point A, you are rotating only on the X axis. Y and Z rotations are still zero.
- As you reach A and go past it, suddenly the computer treats the plane as flying South (I think because the X angle is now closer to South, and moving towards it), and upside down!
If you were just drawing one picture of the aeroplane, this wouldn’t matter. Who cares what the computer uses as the angles (ie whether it thinks you are going North or South, upside down or not), as long as it draws the plane in the right place. And it will draw it in the right place, either way.
The problem is when you are animating the movement, because there is a moment when the computer flips the direction from North to South, and the attitude of the plane from right way up, to upside down. When the computer tries to interpolate between the two quite different situations, it produces very weird visual effects, especially if the plane hovers around the flipping point, so the computer flips back and forward.
Gimbal lock is caused by discontinuity
Gimbal lock in software is caused by discontinuity, an axis suddenly changing by 180 degrees.
This is exactly like the international dateline – you know, that invisible line west of America, where you suddenly go forward or backward a whole day. If you hover over the dateline, you will keep flipping between one day and the next, making a mess of your calendar. And that is very like what is happening above.
The problem is simply that – like dates – Euler angles don’t transition smoothly.
Preventing gimbal lock
So in certain situations, you have to be very careful of turning too far. And, although the example above doesn’t really show this, the problem has a lot to do with only being able to rotate one axis at a time, and it is usually caused when the second axis in your sequence (eg X if the sequence is ZXY) is turned too far.
If you are going to work with Euler angles, then you need to understand gimbal lock and how to avoid it (usually by arranging things so you are extremely unlikely to get two axes lined up together). I’m not going to cover that here, because there is a much better solution.
Quaternions
Quaternions look deceptively simple. They seem to be just vectors with 4 number values, eg (1, 0, 0, 0). What could be so difficult?
However, they are very complex mathematical beasts with all sorts of special arithmetic rules. They were discovered long before computers were invented, but someone clever realised that they could solve the rotation problems described above.
If you are a mathematician, you will be able to understand why they solve these problems, but we are not mathematicians, so we will just have to trust that they work. (I did try – I spent four hours watching videos and several more hours reading books on quaternions, and I still don’t understand them). I’m OK with that – after all, I have a cat I don’t understand, and I get a lot of pleasure from her!
So let’s just focus on what quaternions do.
- a quaternion can store a rotation (x, y and z values). Given the x, y and z rotation angles, we can write a function to calculate a quaternion.
- you can combine rotations – add new rotations to an existing situation – by multiplying quaternions together.
- quaternions rotate all three axes at the same time, instead of one after another, and no matter what you do, there is no discontinuity, removing gimbal lock.
- quaternions are compact, with simple formulae, making them fast and efficient
So quaternions are great for keeping track of our current rotations, and adding to them. But you can’t draw using quaternions, so surely we need to convert back to angles to draw our aeroplane?
Actually, no. What we need is a 4×4 matrix (called modelMatrix by Codea) that encodes the rotations and the offset (translation) from the centre point. This matrix is all we need to draw the plane at a certain point, rotated a certain way.
And there is a way to convert a quaternion to this matrix, using some fairly simple formulae.
Using quaternions
So this is how it all works. Let’s first suppose we have some way of calculating the change to the x, y and z rotation angles, based on the way the user tilts the iPad. I’ll come back to this later, but for now, assume we have some formulae that give us the change to x, y and z. These have nothing to do with quaternions or anything else. We just choose formulae that make the plane fly realistically, so for example, if the user tilts the iPad upward, the x rotation will increase.
These are the steps
- calculate the change to x, y and z rotation angles based on iPad tilt
- convert these changes to a temporary quaternion
- multiply this by the existing quaternion for our plane (to “add” the changes) and store the result
- convert this quaternion into a 4×4 matrix
- set modelMatrix equal to this matrix and draw the plane
The middle step is important because it combines the changes in rotation with the previous rotation values. So the quaternion stores the total of all the changes that have been made so far.
In the next post, I’ll explain how I actually used all this to make my plane fly. It was quite a battle, but hopefully what I learned will make things easier for you, if you ever try this yourself.
Trackbacks & Pingbacks