We can do a slerp interpolation between two quaternions like this:
quat slerp(quat q1, quat q2, float t) {
float angle = acos(dotProduct(q1, q2));
float denom = sin(angle);
//check if denom is zero
return (q1*sin((1-t)*angle)+q2*sin(t*angle))/denom;
}
This will interpolate between the two quaternions the shortest way. However there's also a long way to interpolate between quaternions. As seen in the image below (source Maya).
How do we interpolate the long way?
Linear interpolation between quaternions is called slerp. Quadratic interpolation between quaternions is called squad. Since quaternions are just complex numbers with three imaginary parts, the same equations that work on real numbers and vectors applies to quaternions.
So, let us consider the two quaternions q1 and q2... the angle between them (ang) is given by the following relation: q1(inner)q2 = norm(q1)norm(q2)cos(ang). ang = acos{[q1(inner)q2] / [norm(q1)norm(q2)]}.
The nature of unit quaternions and the way they map to 3D rotations means they can describe each 3D rotation value in two ways - as q(r, v')
and as q(-r, -v')
(imagine them as axis-angle rotations - inverting both the axis and the angle leads to the same 3D rotation).
Quaternions are actually points on a 4D unit spherical surface, and these two values represent anti-podal points on that sphere.
For a slerp (or nlerp) of two quaternions to follow the shortest path, the corresponding 4D points have to lie on the same hemisphere of the 4D sphere (this is also the reason why a weighted average of more than 2 quaternions doesn't have a unique solution). This maps to a non-negative dot product, and is usually something tested for in the interpolation code.
Simply negating one of the source quaternions will give you a point "on the opposite side of the 4D sphere", and lead to interpolation "the long way around" (and explains why negating the interpolation parameter leads to the same result).
This can be done by changing the angle in the spherical interpolation.
In the usual SLERP(q1, q2, t) you get q1 when t=0 and q2 when t=1. The geodesic distance traveled is actually the angle between q1 and q2, which we name theta.
What we want to do here is to travel the complement distance which is 2PI - theta, but in the opposite sense of rotation. We will call this the complement theta.
We want to find a quaternion-valued function Q(t) such that:
SLERP2(q1, q2, t) = q1 Q(t)
when t = 0
SLERP2(q1, q2, 0) = q1 Q(0) = q1
and when t=1
SLERP2(q1, q2, 1) = q1 Q(1) = q2.
So we know that Q(0) = 1 (identity quaternion) and Q(1) = (q1^-1 q2).
It turns out that we can define such a function Q(t) in terms of the exponential map and principal logarithm of quaternions:
Q(t) = Exp(t Log(q1^-1 q2)/2)
You can check that it works by giving values to t like t=0 and t=1.
So far ao good, but that Q(t) will lead us to have the regular SLERP, not the one that we want. Lets take a close look at the logarithm:
Log(q1^-1 q2) = theta V
Where V is a unit vector (actually pure unit quaternion) which is the axis of rotation of quaternion q1^-1 q2. And theta is basically the angle between q1 and q2.
We actually need to change that logarithm so that Q(t) will go the long way, which is the complement theta distance:
Q(t) = Exp(t CompTheta V/2)
Where CompTheta = theta - 2PI.
Recall that exponential function is:
Exp(t CompTheta V/2) = cos(t CompTheta/2) - sin(t CompTheta/2) V
Now, how do we find the Logarithm i.e., theta V?
When you multiply q1^-1 q2 you get a new quaternion, lets call it q12.
q12 = cos(theta/2) - sin(theta/2) V
q12 = w + V'
Where:
w = cos(theta/2)
V' = sin(theta/2) V
theta = 2 atan2(|V'|, w)
V = V'/|V'|
So finally your SLERP2(q1,q2, t) is equal to:
SLERP2(q1,q2, t) = q1 Q(t)
SLERP2(q1,q2, t) = q1 Exp(t CompTheta V/2)
Discraimler: I haven't tested this. If you can test it please comment here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With