Reputation: 24771
I'm looking at two different source for the Slerp method of interpolation between two quaternions. They are pretty similar except for one notable difference: one clamps the dot product between 0 and 1, and the other between -1 and 1. Here is one of them:
glm::fquat Slerp(const glm::fquat &v0, const glm::fquat &v1, float alpha)
{
float dot = glm::dot(v0, v1);
const float DOT_THRESHOLD = 0.9995f;
if (dot > DOT_THRESHOLD)
return Lerp(v0, v1, alpha);
glm::clamp(dot, -1.0f, 1.0f); //<-- The line in question
float theta_0 = acosf(dot);
float theta = theta_0*alpha;
glm::fquat v2 = v1 - v0*dot;
v2 = glm::normalize(v2);
return v0*cos(theta) + v2*sin(theta);
}
Here is the other:
template <typename T>
inline QuaternionT<T> QuaternionT<T>::Slerp(T t, const QuaternionT<T>& v1) const
{
const T epsilon = 0.0005f;
T dot = Dot(v1);
if (dot > 1 - epsilon) {
QuaternionT<T> result = v1 + (*this - v1).Scaled(t);
result.Normalize();
return result;
}
if (dot < 0) //<-The lower clamp
dot = 0;
if (dot > 1)
dot = 1;
T theta0 = std::acos(dot);
T theta = theta0 * t;
QuaternionT<T> v2 = (v1 - Scaled(dot));
v2.Normalize();
QuaternionT<T> q = Scaled(std::cos(theta)) + v2.Scaled(std::sin(theta));
q.Normalize();
return q;
}
I think it is worth noting also the the Lerp
algorithm in the second one doesn't seem right for all cases?
I just want some feedback on these differences and if they really matter at all.
Upvotes: 3
Views: 2047
Reputation: 6525
If you're doing slerp
between two quaternions q1
and q2
, even if you normalize them both right before passing them to the function, floating point discrepancies can make it so that their inner-product ends up being slightly greater than one or less than negative one. This will make acos
crash. Of course, in both code snippets you provide, if dot(q1,q2)>1
, the code does linear interpolation and returns immediately. So clamping to +1 is unnecessary. I don't see any particular problem with the Lerp
in the second example.
For both cases, clamping to 0 or to -1 should usually be unnecessary and might be a Bad Idea.
The main thing is that if the inner product of two quaternions is negative, interpolating between them means going the long way around. To clarify, if the angle between two lines is 30 degrees, you can also interpolate one line onto the other by rotating 330 degrees. The same hold for orientation space.
Unit quaternions are a 2x redundant representation of orientation; so if the inner-product between two quaternions is less-than zero, you normally negate all of the elements of one of them before you interpolate.
If you do want to interpolate the long way around, clamping to -1 is right. Clamping the dot-product to zero will break things pretty badly. If you don't want to interpolate the long way around, you should always ensure a non-negative inner-product before passing your quaternions to either of these functions, since they don't do it for you. So clamping to zero would be unnecessary.
Upvotes: 4