Dual Quaternion Slerp

Hey All,

I’m implementing a skinning algorithm using dual quaternions to represent the translation/rotation of my joints a la wscg.zcu.cz/wscg2012/short/A29-full.pdf. However, I’m getting some errors during interpolation, and I suspect it’s due to the fact that my quaternions aren’t being normalized.

Here’s the deal: I understand how to use quaternions to represent rotation, and I understand why slerp works in this case. In order for us to use slerp we need a unit quaternion, and the rotation quaternion is unit (unit vector for axis, sin2+cos2 = 1).

However, I don’t believe that you can use slerp for the translation component (aka dual part) because it is not a unit quaternion on its own. We could normalize them, but if we did then we’d be losing the original translation information we wanted to store. We could store the translation magnitude, normalize the translation quaternion, and multiply it back in after slerp finishes, but then we’d have to linearly interpolate the magnitude, hence defeating the purpose of slerp.

As of now, using glm, I’ve got the following slerp function:


using namespace glm; //not really but you get the idea

fdualquat dualSlerp(fdualquat a, fdualquat b, float x){
   fdualquat ret;
   ret.real = slerp(a.real,b.real,x);
   ret.dual = lerp(a.dual,b.dual,x);
   return normalize(ret);
}

However, this feels dirty; there’s no way I can combine slerp and lerp in the same function. However, I can’t slerp the dual part because it’s magnitude is greater than 1,

To be honest, I don’t see the point of dual quaternions at all if this is the case. Since we’ve got 8 floats in a dual quaternion, why not use one quaternion for rotation and one vec4 for translation? We could even use a vec3, but the one number isn’t the issue here. Why are dual quaternions used at all?

The advantage of dual quaternions is that they can be composed easily, the algebra just works.

If you use a quaternion + vector representation, you basically decompose your transform into a rotation r followed by a translation t. If you want to combine two transforms, you have to be careful: generally r1t1 * r2t2 != r1r2 t1t2, so you can’t simply combine the rotation and translation components.

Alright, I can see how that would make things easier to chain. Thanks.

However, given all that, is my SLERP code correct?

Sorry, can’t help directly, but this paper by the same author as the one you linked above seems to have more details on interpolation. From a quick glance interpolation is a more involved operation than your implementation suggests :wink:

Yeesh. Well, alright, I’ll press on.

I’ll stick with Dual Quaternions because I like the math. In theory, though, couldn’t I define a class with a quaternion member (rotation) and a vec4 member (translation), overload the * operator to multiply the quaternion and add the translation, and use that to chain my transforms?


class QuatVec{
public:
   fquat rot;
   vec3 trans;
   QuatVec(): rot(), trans(){}
   QuatVec(fquat r, vec3 t): rot(glm::normalize(r)), trans(t) {}
   QuatVec operator * (const QuatVec& other){
      return QuatVec(rot*other.rot, trans+other.trans);
   }
   QuatVec blend(const QuatVec& other, float x){
      return QuatVec(glm::slerp(rot,other.rot,x),glm::mix(trans,other.trans,x));
   }
   mat4 toMat4(){
      return glm::translate(trans)*glm::mat4_cast(rot);
   }
};

meh, it doesn’t seem like anyone cares. In the end I realized that I shouldn’t be doing any of this stuff, since I have multiple keyframes I need to run through with different time intervals in between. Given all that I’ve decided to look into Centripetal Catmull-Rom Splines, which seem to be a Cubic Spline that gets parameterized according to the Lagrange Interpolants.