Quaternion slerp: ensure "short path" when falling back to lerp.
I noticed that our slerp function sometimes produces a jolt in animation, but only when the time delta is very small, and only when the two operands have completely opposing signs. For example, let's say you are slerping from <0.76, 0.39, 0.51, 0.19> to <-0.72, -0.45, -0.49, -0.17>. These quats are actually quite near to each other because the total negation of the second quat is similar to the first quat. We were already doing the short path check in the proper slerp path, but not when falling back to lerp due to a small angle.
This commit is contained in:
@@ -251,7 +251,7 @@ public:
|
||||
static constexpr T value_eps = T(10) * std::numeric_limits<T>::epsilon();
|
||||
// Prevent blowing up when slerping between two quaternions that are very near each other.
|
||||
if ((T(1) - absd) < value_eps) {
|
||||
return normalize(lerp(p, q, t));
|
||||
return normalize(lerp(d < 0 ? -p : p, q, t));
|
||||
}
|
||||
const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q||
|
||||
const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1)));
|
||||
|
||||
@@ -273,6 +273,20 @@ TEST_F(QuatTest, ArithmeticFunc) {
|
||||
qs = slerp(qa, qb, 0.5);
|
||||
EXPECT_NEAR(qs[3], -0.92, 0.1);
|
||||
EXPECT_NEAR(qs[2], +0.38, 0.1);
|
||||
|
||||
// Create two quats that are near to each other, but with opposite signs.
|
||||
qa = { 0.76, 0.39, 0.51, 0.19};
|
||||
qb = {-0.759, -0.385, -0.50, -0.19};
|
||||
qs = slerp(qa, qb, 0.5);
|
||||
|
||||
// The rotation angle produced by v * slerp(A, B, .5) should be between the rotation angles
|
||||
// produced by (v * A) and (v * B).
|
||||
double3 v(0, 0, 1);
|
||||
double3 va = qa * v;
|
||||
double3 vb = qb * v;
|
||||
double3 vs = qs * v;
|
||||
EXPECT_LT(dot(v, va), dot(v, vs));
|
||||
EXPECT_LT(dot(v, vs), dot(v, vb));
|
||||
}
|
||||
|
||||
TEST_F(QuatTest, MultiplicationExhaustive) {
|
||||
|
||||
Reference in New Issue
Block a user