Quaternion Slerp and Lerp implementation (with overshoot)

I am working with Unity (C#) and was looking for an implementation of Slerp and possibly Lerp that allow overshoot (progress not strictly limited to 0..1 range). I want to do some tweening animation using an elastic ease-out function. My ease-out function returns values slightly outside the 0..1 range in order to get an oscillating-around-the-target effect. The problem is that Unity's Quaternion Lerp and Slerp functions appear to clamp the progress input in the 0..1 range.

Implementation code in any language would be helpful. I don't know enough about Quaternion math to write this myself.

• Have tried applying your elastic easing function to the euler angles? – Kelly Thomas Apr 11 '15 at 11:39

Here’s a little example. Suppose you slerp q0 and q1 with a value of t = 0.2 and a value of t = 0.4. This gives you a quaternion s(0.2) and a quaternion s(0.4). Quaternion multiplication also gives us the following:

s(0.2) = q0 * inverse(q0) * s(0.2)
'——————— K ————————'


So we have a value K which, when right-multiplied with q0, gives us s(0.2). Now intuitively we can guess that multiplying twice will give us s(0.4):

s(0.4) = q0 * inverse(q0) * s(0.2) * inverse(q0) * s(0.2)
'——————— K ————————'   '——————— K ————————'


Simplifying:

s(0.4) = s(0.2) * inverse(q0) * s(0.2)


We can use this technique to slerp with values greater than 1 by just dividing by two. Which brings us to the following pseudocode:

quat slerp_generic(quat q0, quat q1, float t)
{
// If t is too large, divide it by two recursively
if (t > 1.0)
{
quat tmp = slerp_generic(q0, q1, t / 2);
return tmp * inverse(q0) * tmp;
}

// It’s easier to handle negative t this way
if (t < 0.0)
return slerp_generic(q1, q0, 1.0 - t);

return slerp(q0, q1, t);
}


Edit: the above code works great for slerp; for lerp however you’re going to get discontinuities in the derivative which you may not want. This code uses a similar technique and will work better for lerp:

quat lerp_generic(quat q0, quat q1, float t)
{
// If t is too large, subtract 1.0 from it recursively
if (t > 1.0)
{
quat tmp = lerp_generic(q0, q1, t - 1.0);
return tmp * inverse(q0) * q1;
}

// It’s easier to handle negative t this way
if (t < 0.0)
return lerp_generic(q1, q0, 1.0 - t);

return lerp(q0, q1, t);
}

• This is a nice little gem. – teodron Jun 11 '15 at 20:09