Latest ArticlesEngine Architecture #1
Static Shadows #1
Paint on Mesh
A Tale of the Internet
Quaternion Math Sep 14, 2006
Ever since I posted my quaternion class article, with only the explanation: "quaternions use four numbers to store rotations," I've felt like I should do a more in-depth article. I freely admit that I'm not an expert who understands quaternions on a deep mathematical level, but I think I can still give some useful information.
If you mainly just use quats for storing an orientation, you probably don't need to know anything about them. However if you're doing a lot of quaternion interpolation in your program, it's good to have some idea of how they work.
I originally wanted to make a simple quaternion visualization program to compliment this article. Something so you could see quaternion orientation, the component values, and some kind of interpolation graph. I realized I wouldn't have time for that and an article, but it may be a future article.
OverviewQuaternions can be thought of as 4D vectors, or as a 3D vector(x,y,z) and a scalar(w). They can store scale as well as rotation, but are usually just used for rotation. Pure rotational quaternions are of unit length. The valid values for these quats can be represented as the surface of a 4D unit sphere.
We can normalize a quaternion to unit length in the same manner we normalize a 3D vector.
Some other common vector operations have a similar use and implementation for quaternions, such as the dot product, and linear interpolation.
You can review an implementation of these functions in the sample quaternion class code.
Interpolation MethodsTwo possibilities for interpolation along the shortest arc between quaternions are spherical lerp and normalized lerp. (lerp is common shorthand for linear interpolation.) You can find an implementation of "Slerp" and "Nlerp" in the code.
They both use a weighted sum of the two quat vectors, and they both travel through the same weights. (Since normalizing a quat simply scales each component, which would be the same as scaling the weights to valid Slerp weights. ) The only difference is the rate of travel, Slerp rotates at a constant rate with respect to t, normalized lerp doesn't. I actually tried adding a check box to globally switch between the two methods for animated characters, and I was never sure if I could even tell the difference.
In my program I have smoothed & curve-based interpolation methods aren't based on quaternions( Inverse Kinematics with target positions, Euler angles for the limited purposes they work well for... ) So I'm not an expert in quaternion curves. Two options are to either use "Squad" for cubic interpolation or to just apply curves to the individual quaternion components and then normalize. Or if it's something like just smoothing a reverse in rotation direction you can apply a curve to the interpolation of t from 0 to 1.
Rotation RangeA 3x3 rotation matrix( ie. 3 orthonormal basis vectors ) can only store 360 degrees of rotation. Quaternions can actually store 720 degrees of rotation. This means there are two different values for the components of a quaternion that represent the same rotation in 3d space. You can see why this is if you look at the code for setting a quaternion from an axis-angle. It uses the half-angle of sine and cosine, so it only repeats every 720 degrees.
Specifically, for any quaternion, Quat( x, y, z, w ) is the "same" rotation as Quat( -x, -y, -z, -w ). That this is true for a single axis is apparent from the fact that sin(theta) = -sin(theta+180degrees). That the actual component numbers aren't the same is important in interpolation. If you have an identity quaternion, and a quaternion rotated 270 degrees around the X-Axis (this is just a random example, I could have chosen anything), Slerp between these two quaternions will rotate 270 degrees around the X-Axis, when the closest path is actually 90 degrees, from "360" to 270.
We can make sure it always follows the shortest path by using the dot product to see if the two quats we're interpolating between are in the same neighbourhood. (This is done the same way as using the dot product to check the angle between 3d vectors.) If the dot product is negative, we flip the sign on all the components of one of the quaternions before interpolating. Note that with Slerp if you flip the sign on a quat after computing cosTheta, you also need to flip the sign on cosTheta.
There will always be a pole at which a slight change in rotation will be enough to cause the interpolation to follow a completely different path. With 720 degree coverage, the pole will be at 360 degrees between the two quats. If you flip based on the dot product, the pole is at 180 degrees. I've found both 360 and 720 degree coverage can be useful in certain situations.
External LinksRotating objects using quaternions - A Gamasutra article from 1998 that's more code-oriented than Wikipedia.
Wikipedia Quaternion Entry - If you don't mind reading a long page with a lot of math, Wikipedia has a good entry for quaternions, and many other technical/mathematic subjects.