Updates
1.2 A minor correction with the formula of converting from Quat to Axis. The scale is missing a square root. Thanks to Shi for pointing that out. From version 1.0 - 1.1 The norm of a quaternion should be the square root of the q.q. The mistake was brought to my attention by several kind readers and upon checking the definition of the Euclidean properties for complex numbers, I realize the norm property [bquote][color=#000080][font=courier new,courier,monospace]|| u+v || <= || u || + || v ||
[/font][/color][/bquote] is violated for the previous definition of the magnitude. The code in the samples are updated as well.Foreword To me, the term 'Quaternion' sounds out of this world, like some term from quantum theory about dark matter, having dark secret powers. If you, too, are enthralled by this dark power, this article will bring enlightenment (I hope). The article will show you how to do rotations using quaternions, and bring you closer to understanding quaternions (and their powers). If you do spot a mistake please email me at [email="robin@cyberversion.com"]robin@cyberversion.com[/email]. Also if you intend to put this on your site, please send me a mail. I like to know where this ends up.
Why use Quaternions? To answer the question, let's first discuss some common orientation implementations.
Euler representation This is by far the simplest method to implement orientation. For each axis, there is a value specifying the rotation around the axis. Therefore, we have 3 variables [bquote]
[font=Courier New][color=#000080]x, y, z <-- angle to rotate around global coordinate axis [/color][/font]
[/bquote] that vary between 0 and 360 degrees (or 0 - 2pi). They are the roll, pitch, and yaw (or pitch, roll, and yaw - whatever) representation. Orientation is obtained by multiplying the 3 rotation matrices generated from the 3 angles together (in a specific order that you define). Note: The rotations are specified with respect to the global coordinate axis frame. This means the first rotation does not change the axis of rotation for the second and third rotations. This causes a situation known as gimbal lock, which I will discuss later.Angle Axis representation This implementation method is better than the Euler representation as it avoids the gimbal lock problem. The representation consists of a unit vector representing an arbitrary axis of rotation, and another variable (0 - 360) representing the rotation around the vector: [bquote][font=Courier New][color=#000080]
x, y, z <-- unit vector representing arbitrary axis angle <-- angle to rotate around above axis
[/color][/font][/bquote] Why are these representations bad?Gimbal Lock As rotations in the Euler representation are done with respect to the global axis, a rotation in one axis could 'override' a rotation in another, making you lose a degree of freedom. This is gimbal lock. Say, if the rotation in the Y axis rotates a vector (parallel to the x axis) so that the vector is parallel to the z axis. Then, any rotations in the z axis would have no effect on the vector. Later, I will show you an example of gimbal lock and how you can use quaternions to overcome it.
Interpolation Problems Though the angle axis representation does not suffer from gimbal lock, there are problems when you need to interpolate between two rotations. The calculated interpolated orientations may not be smooth, so you will get jerky rotation movements. Euler representation suffers from this problem as well.
Let's get started Before we begin, let's establish some assumptions I'll be making. I hate the way many articles leave this important section out, causing a great deal of confusion when it comes to the mathematics. Coordinate System - This article assumes a right hand coordinate system, like OpenGL. If you are using a left handed coordinate system like Direct3D, you may need to transpose the matrices. Note that the Direct3D samples have a quaternion library already, though I recommend you check through their implementation before using it. Rotation Order - The sequence of rotations in the Euler representation is X, then Y, and then Z. In matrix form: [bquote][font=Courier New][color=#000080]
RotX * RotY * RotZ <-- Very Important
[/color][/font][/bquote] Matrix - Matrices are in column major format, like they are in OpenGL. [bquote][font=Courier New][color=#000080]Example[nbsp][[nbsp]0[nbsp][nbsp]4[nbsp][nbsp]8[nbsp][nbsp]12 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]1[nbsp][nbsp]5[nbsp][nbsp]9[nbsp][nbsp]13 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2[nbsp][nbsp]6[nbsp][nbsp]10[nbsp]14 [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]3[nbsp][nbsp]7[nbsp][nbsp]11[nbsp]15[nbsp]][/color][/font]
[/bquote] Vectors and Points - Implemented as a 4x1 matrix so applying a transformation is of the order [bquote][font=Courier New][color=#000080]Rotation[nbsp]Matrix*[nbsp][nbsp][[nbsp]vx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]vy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]vz [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]1[nbsp][nbsp]][nbsp][nbsp]<--[nbsp]4x1[nbsp]vector[/color][/font]
[/bquote] This does not imply that I prefer OpenGL over Direct3D. It just happened that I learned OpenGL first, and so my quaternion knowledge was gained in OpenGL. Note: If you specify rotations in another order, certain quaternion functions will be implemented differently, especially those that deal with Euler representation.What is a Quaternion?
A complex number is an imaginary number that is defined in terms of i, the imaginary number, which is defined such that i * i = -1.
A quaternion is an extension of the complex number. Instead of just i, we have three numbers that are all square roots of -1, denoted by i, j, and k. This means that
[bquote]j * j = -1
k * k = -1[/bquote]
So a quaternion can be represented as
[bquote]q = w + xi + yj + zk[/bquote]
where w is a real number, and x, y, and z are complex numbers.
Another common representation is
[bquote]q=[ w,v ][/bquote]
where v = (x, y, z) is called a "vector" and w is called a "scalar". Although the v is called a vector, don't think of it as a typical 3 dimensional vector. It is a vector in 4D space, which is totally unintuitive to visualize.
Identity Quaternions
Unlike vectors, there are two identity quaternions.
The multiplication identity quaternion is
[bquote]q= [1,(0, 0, 0)][/bquote]
So any quaternion multiplied with this identity quaternion will not be changed.
The addition identity quaternion (which we do not use) is
[bquote]q= [0,(0, 0, 0)][/bquote]
Using quaternions as orientations
The first thing I want to point out is that quaternions are not vectors, so please don't use your preconceived vector mathematics on what I'm going to show.
This is going to get very mathematical, so please bear with me.
We need to first define the magnitude of a quaternion.
[bquote]|| q ||= Norm(q) =sqrt(w[sup]2[/sup] + x[sup]2[/sup] + y[sup]2[/sup] + z[sup]2[/sup])[/bquote]
A unit quaternion has the following property
[bquote]w[sup]2[/sup] + x[sup]2[/sup] + y[sup]2[/sup] + z[sup]2[/sup]=1[/bquote]
So to normalize a quaternion q, we do
[bquote]q = q / || q || = q / sqrt(w[sup]2[/sup] + x[sup]2[/sup] + y[sup]2[/sup] + z[sup]2[/sup])[/bquote]
What is so special about this unit quaternion is that it represents an orientation in 3D space. So you can use a unit quaternion to represent an orientation instead of the two methods discussed previously. To use them as orientations, you will need methods to convert them to other representations (e.g. matrices) and back, which will be discussed soon.
Visualizing a unit quaternion You can visualize unit quaternions as a rotation in 4D space where the (x,y,z) components form the arbitrary axis and the w forms the angle of rotation. All the unit quaternions form a sphere of unit length in the 4D space. Again, this is not very intuitive but what I'm getting at is that you can get a 180 degree rotation of a quaternion by simply inverting the scalar (w) component. Note: Only unit quaternions can be used for representing orientations. All discussions from here on will assume unit quaternions.
Conversion from Quaternions To be able to use quaternions effectively, you will eventually need to convert them to some other representation. You cannot interpret keyboard presses as quaternions, can you? Well, not yet.
Quaternion to Matrix Since OpenGL and Direct3D allow rotations to be specified as matrices, this is probably the most important conversion function, since homogenous matrices are the standard 3D representations. The equivalent rotation matrix representing a quaternion is [bquote]
[font=Courier New][color=#000080]Matrix[nbsp]=[nbsp][nbsp][[nbsp]w[sup]2[/sup][nbsp]+[nbsp]x[sup]2[/sup][nbsp]-[nbsp]y[sup]2[/sup][nbsp]-[nbsp]z[sup]2[/sup][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]-[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]+[nbsp]2wy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]+[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]w[sup]2[/sup][nbsp]-[nbsp]x[sup]2[/sup][nbsp]+[nbsp]y[sup]2[/sup][nbsp]-[nbsp]z[sup]2[/sup][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]-[nbsp]2wx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]-[nbsp]2wy[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]+[nbsp]2wx[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]w[sup]2[/sup][nbsp]-[nbsp]x[sup]2[/sup][nbsp]-[nbsp]y[sup]2[/sup][nbsp]+[nbsp]z[sup]2[/sup][nbsp]][/color][/font]
[/bquote] Using the property of unit quaternions that w[sup]2[/sup] + x[sup]2[/sup] + y[sup]2[/sup] + z[sup]2[/sup] = 1, we can reduce the matrix to [bquote][font=Courier New][color=#000080]Matrix[nbsp]=[nbsp][nbsp][[nbsp]1[nbsp]-[nbsp]2y[sup]2[/sup][nbsp]-[nbsp]2z[sup]2[/sup][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]-[nbsp]2wz[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]+[nbsp]2wy [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xy[nbsp]+[nbsp]2wz[nbsp][nbsp][nbsp][nbsp]1[nbsp]-[nbsp]2x[sup]2[/sup][nbsp]-[nbsp]2z[sup]2[/sup][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]-[nbsp]2wx [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2xz[nbsp]-[nbsp]2wy[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2yz[nbsp]+[nbsp]2wx[nbsp][nbsp][nbsp][nbsp]1[nbsp]-[nbsp]2x[sup]2[/sup][nbsp]-[nbsp]2y[sup]2[/sup][nbsp]][/color][/font]
[/bquote]Quaternion to Axis Angle To change a quaternion to a rotation around an arbitrary axis in 3D space, we do the following: [bquote]
[font=Courier New][color=#000080]If[nbsp]the[nbsp]axis[nbsp]of[nbsp]rotation[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp](ax,[nbsp]ay,[nbsp]az) and[nbsp]the[nbsp]angle[nbsp]is[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]theta[nbsp](radians) then[nbsp]the[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]angle=[nbsp]2[nbsp]*[nbsp]acos(w) [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]ax=[nbsp]x[nbsp]/[nbsp]scale [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]ay=[nbsp]y[nbsp]/[nbsp]scale [nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]az=[nbsp]z[nbsp]/[nbsp]scale where[nbsp]scale[nbsp]=[nbsp]sqrt[nbsp](x[sup]2[/sup][nbsp]+[nbsp]y[sup]2[/sup][nbsp]+[nbsp]z[sup]2[/sup])[/color][/font]
[/bquote] Another variation I have seen is that the scale = sin(acos(w)). They may be equivalent, though I didn't try to find the mathematical relationship behind them. Anyway if the scale is 0, it means there is no rotation so unless you do something, the axis will be infinite. So whenever the scale is 0, just set the rotation axis to any unit vector with a rotation angle of 0.A Simple Example In case you are getting confused with what I'm getting at, I will show you a simple example here. Say the camera orientation is represented as Euler angles. Then, in the rendering loop, we position the camera using [bquote][font=Courier New][color=#000080]
RotateX * RotateY * RotateZ * Translate
[/color][/font][/bquote] where each component is a 4x4 matrix. So if we are using a unit quaternion to represent the camera orientation, we have to convert the quaternion to a matrix first [bquote][font=Courier New][color=#000080]Rotate (from Quaternion) * Translate
[/color][/font][/bquote] A more specific example in OpenGL: EulerQuaternion[font=courier new,courier,monospace][color=#000080]glRotatef( angleX, 1, 0, 0)[/color]
[/font][color=#000080][font=courier new,courier,monospace]
glRotatef( angleY, 0, 1, 0)
[/font][/color][color=#000080]
[font=courier new,courier,monospace]glRotatef( angleZ, 0, 0, 1)[/font]
[/color][color=#000080]
[font=courier new,courier,monospace]// translate[/font]
[/color][color=#000080][font=courier new,courier,monospace]// convert Euler to quaternion[/font]
[/color][color=#000080][font=courier new,courier,monospace]
// convert quaternion to axis angle
[/font][/color][color=#000080]
[font=courier new,courier,monospace]glRotate(theta, ax, ay, az)[/font]
[/color][color=#000080]
I explain where the quaternion-to-rotationi-matrix formula came from in this blog post: http://blog.saadtaame.org/2016/09/matrix-representation-of-quaternion.html. You might find that useful.