Reputation: 51
I've been studying Quaternions for an upcoming project and have come across a conceptual problem that I can't wrap my head around.
The way to normalize a quaternion is as follows:
q_mag = sqrt(q0^2+q1^2+q2^2+q3^2)
q0 = q0/q_mag
q1 = q1/q_mag
q2 = q2/q_mag
q3 = q3/q_mag
Straight forward and just like normalizing any other vector. But my question is how does this normalization method retain the same rotation information. Using the definition of a quaternion representing an axis-angle representation like below,
angle = 2 * acos(q0)
x = qx / sqrt(1-q0*q0)
y = qy / sqrt(1-q0*q0)
z = qz / sqrt(1-q0*q0)
Since the normalization operation scales the x,y,z values equally, the axis around which you are rotating never changes. But the value of the angle itself changes drasticly with a normalization operation.
So wouldn't it make more sense to use a method that preserves the value of q0 and only adjusts the other points to reach normalization?
Upvotes: 5
Views: 3174
Reputation: 127
I know this is a bit of a necro, sorry, but i think this might be useful for future readers.
Unless I'm thoroughly mistaken, it's actually preferable not to preserve the angle when normalizing to account for rounding errors (in the context of using quaternions to represent rotations, especially in games). Let me explain why:
let's say you have two quaternions (I'll refer to them as Q1
and Q2
from here on out) that are supposed to represent rotations, but are not unit due to a rounding error, and you want to multiply them (I'll call the result Q3
. We also want this to be a unit quaternion). Let's suppose t1
is a variable, that, when multiplied with every component of Q1
, Q1
becomes a unit quaternion (that means t1
is one divided by the euclidean length of Q1
, but that really is not relevant here. t2
does the same thing for Q2
. If we now multiply the quaternions post-normalizing (ie Q3 = (t1*Q1)(t2*Q2)
)we get the following:
Q3.w = t1*Q1.w*t2*Q2.w - t1*Q1.x*t2*Q2.x - t1*Q1.y*t2*Q2.y - t1*Q1.z*t2*Q2.z
Q3.x = t1*Q1.w*t2*Q2.x + t1*Q1.x*t2*Q2.w + t1*Q1.y*t2*Q2.z - t1*Q1.z*t2*Q2.y
Q3.y = t1*Q1.w*t2*Q2.y - t1*Q1.x*t2*Q2.z + t1*Q1.y*t2*Q2.w + t1*Q1.z*t2*Q2.x
Q3.z = t1*Q1.w*t2*Q2.z + t1*Q1.x*t2*Q2.y - t1*Q1.y*t2*Q2.x + t1*Q1.z*t2*Q2.w
which can be rewritten as
Q3.w = (t1*t2)*(Q1.w*Q2.w - Q1.x*Q2.x - Q1.y*Q2.y - Q1.z*Q2.z)
Q3.x = (t1*t2)*(Q1.w*Q2.x + Q1.x*Q2.w + Q1.y*Q2.z - Q1.z*Q2.y)
Q3.y = (t1*t2)*(Q1.w*Q2.y - Q1.x*Q2.z + Q1.y*Q2.w + Q1.z*Q2.x)
Q3.z = (t1*t2)*(Q1.w*Q2.z + Q1.x*Q2.y - Q1.y*Q2.x + Q1.z*Q2.w)
in other words, Q3=(t1*Q1)(t2*Q2)=(t1*t2)(Q1*Q2)
. As you can see, normalizing in this way after the multiplication results in the same quaternion as normalizing both inputs pre-multiplication. This means we only have to normalize right before we apply the rotation to a vector/mesh/point, instead of after every calculation, since it produces the same result regardless of how far the quaternion drifts from being unit.
Now let's look at the same calculation, but with an angle-preserving way of making a quaternion unit (the t
variables now make the quaternion unit when multiplied with only the non-real (aka xyz) parts):
Q3.w = Q1.w*Q2.w - t1*Q1.x*t2*Q2.x - t1*Q1.y*t2*Q2.y - t1*Q1.z*t2*Q2.z
Q3.x = Q1.w*Q2.x + t1*Q1.x*Q2.w + t1*Q1.y*t2*Q2.z - t1*Q1.z*t2*Q2.y
Q3.y = Q1.w*Q2.y - t1*Q1.x*t2*Q2.z + t1*Q1.y*Q2.w + t1*Q1.z*t2*Q2.x
Q3.z = Q1.w*Q2.z + t1*Q1.x*t2*Q2.y - t1*Q1.y*t2*Q2.x + t1*Q1.z*Q2.w
notice how the non-real components of Q3
no longer have a common factor This means that normalizing Q3
post-operation in this way may result in a different quaternion than the one you'd get if you normalized Q2
and Q1
in this way pre-operation.
Normalizing a quaternion is not a cheap operation, so it is preferable to use the non-angle-preserving way for stuff like games, since you'd need to use it less often, especially when compositing a large number of rotations. I'm not sure if the same holds up for other quaternion operations, but considering you'll probably be multiplying quaternions alot it is, at least in my opinion, preferable to use the non-angle-preserving way.
Upvotes: 1
Reputation: 876
Math answer: A unit quaternion represents a rotation in 3D space. Any other (i.e.: non-unit) quaternion does not represent a rotation, so the formula angle = 2 * acos(q0) does not apply to these quaternions. So there is no change of angle when normalising, because the quaternions that you would normalise do not represent rotations in the first place.
Programming answer: Floating point operations have accuracy issues. These issues result in small errors, which if accumulated may become large errors. When multiplying two unit quaternions, the mathematical result is another unit quaternion. However the floating point implementation of unit quaternions multiplication may result in a quaternion with a norm close to 1 but not equal 1. In this case we shall normalise the quaternion to correct the error. When we normalise we divide q0 by the norm which is very close to 1, so there is no major change in the value of q0. Because we normalise early the norm is always very close to 1 and we don't need to worry about the precision.
Late answer, but I hope it help.
Upvotes: 5