Wrong Blending of 4 Dual Quaternions

Hi. I’m trying to move from matrices to dual quaternions (for skinning in Vertex Shader), but I’m in stuck… in trouble… Really I don’t know where the problem reside.
Look at this comparison (matrices vs dual quaternions): http://imgur.com/a/iX8AA
ALL the dual quaternions uploaded (I’m sure) are normalized. I upload the dual quaternions as an array of mat2x4 uniforms.
Here’s the code that DO NOT works:


                vec4     w    = in_Weights;
                mat2x4  dq0 = mat2x4(bonesParams[in_Bones.x]);
                mat2x4  dq1 = mat2x4(bonesParams[in_Bones.y]);
                mat2x4  dq2 = mat2x4(bonesParams[in_Bones.z]);
                mat2x4  dq3 = mat2x4(bonesParams[in_Bones.w]);

                if (dot(dq0[0], dq1[0]) <= 0.0) dq1*= -1; // Antipodality correction.
                if (dot(dq0[0], dq2[0]) <= 0.0) dq2*= -1;	
                if (dot(dq0[0], dq3[0]) <= 0.0) dq3*= -1;
                mat2x4 blendedDQ  = w.x * dq0;
                       blendedDQ += w.y * dq1;
                       blendedDQ += w.z * dq2;
                       blendedDQ += w.w * dq3;
                float invLen = inversesqrt ( dot(blendedDQ[0], blendedDQ[0] );
                mat3 skinRot =  invLen * q_getMat3( blendedDQ[0] );
                vec3 skinTra =  invLen * dq_getTranslation( blendedDQ[0], blendedDQ[1] );


In order to test that the q_getMat3() and dq_getTranslation() routines are correct, I made the following modification, converting all 4 quaternion and dual part to matrices and vectors, and blending them instead (and this give me correct results but is naturally slower ):


                mat3 m = q_getMat3(dq0[0]) * w.x + q_getMat3(dq1[0]) * w.y + q_getMat3(dq2[0]) * w.z + q_getMat3(dq3[0]) * w.w;
                vec3 t = dq_getTranslation(dq0[0], dq0[1]) * w.x + dq_getTranslation(dq1[0], dq1[1]) * w.y +
                         dq_getTranslation(dq2[0], dq2[1]) * w.z + dq_getTranslation(dq3[0], dq3[1]) * w.w;
                skinRot = m;
                skinTra =  t;

Dual Quaternions should be the efficient way to blend rigid transform but I’m missing some piece of the puzzle.
Any help is appreciated. Thanks in advance.

Your normalizing should be applied to the dual quats themselves. Try:


                float invLen = inversesqrt ( dot(blendedDQ[0], blendedDQ[0] );
                blendedDQ[0] *= invLen;
                blendedDQ[1] *= invLen;
                mat3 skinRot =  q_getMat3( blendedDQ[0] );
                vec3 skinTra =  dq_getTranslation( blendedDQ[0], blendedDQ[1] );

Thanks, I tried as you suggested but nothing changed at all. If this is the right algorithm to blend Dual Quaternions, maybe I’m doing something wrong elsewhere.

This are my quaternion / dual quaternion routines:


vec4 q_matrixToQuat(mat3 m){
    vec4 q;
        if ( m[0].x + m[1].y + m[2].z > 0.0f ) {
            float t = + m[0].x + m[1].y + m[2].z + 1.0f;
            float s = inversesqrt( t ) * 0.5f;
            q[3] = s * t;
            q[2] = ( m[0].y - m[1].x ) * s;
            q[1] = ( m[2].x - m[0].z ) * s;
            q[0] = ( m[1].z - m[2].y ) * s;
        } else if ( m[0].x > m[1].y && m[0].x > m[2].z ) {
            float t = + m[0].x - m[1].y - m[2].z + 1.0f;
            float s = inversesqrt( t ) * 0.5f;
            q[0] = s * t;
            q[1] = ( m[0].y + m[1].x ) * s;
            q[2] = ( m[2].x + m[0].z ) * s;
            q[3] = ( m[1].z - m[2].y ) * s;
        } else if ( m[1].y > m[2].z ) {
            float t = - m[0].x + m[1].y - m[2].z + 1.0f;
            float s = inversesqrt( t ) * 0.5f;
            q[1] = s * t;
            q[0] = ( m[0].y + m[1].x ) * s;
            q[3] = ( m[2].x - m[0].z ) * s;
            q[2] = ( m[1].z + m[2].y ) * s;
        } else {
            float t = - m[0].x - m[1].y + m[2].z + 1.0f;
            float s = inversesqrt( t ) * 0.5f;
            q[2] = s * t;
            q[3] = ( m[0].y - m[1].x ) * s;
            q[0] = ( m[2].x + m[0].z ) * s;
            q[1] = ( m[1].z + m[2].y ) * s;
        }
    return q;
}


mat3 q_quatToMatrix(vec4 q){
    return mat3( 1.0 - 2.0*(q.y*q.y + q.z*q.z), 2.0*((q.x*q.y) + q.z*q.w), 2.0*((q.x*q.z) - q.y*q.w),
                 2.0*((q.x*q.y) - q.z*q.w), 1.0-2.0* (q.x*q.x + q.z*q.z), 2.0*((q.y*q.z) + q.x*q.w),
                 2.0*((q.x*q.z) + q.y*q.w), 2.0* ((q.y*q.z) - q.x*q.w), 1.0-2.0*  (q.x*q.x + q.y*q.y) );
}

C++ code Translation to Dual Part:


                    dualPart[0] = -0.5f*( translation[0]*unit_quat[1] + translation[1]*unit_quat[2] + translation[2]*unit_quat[3]);
                    dualPart[1] =  0.5f*( translation[0]*unit_quat[0] + translation[1]*unit_quat[3] - translation[2]*unit_quat[2]);
                    dualPart[2] =  0.5f*(-translation[0]*unit_quat[3] + translation[1]*unit_quat[0] + translation[2]*unit_quat[1]);
                    dualPart[3] =  0.5f*( translation[0]*unit_quat[2] - translation[1]*unit_quat[1] + translation[2]*unit_quat[0]);

Extract translation from Dual Part:


// dual quaternion
 vec3 dq_getTranslation( vec4 real, vec4 dual ){	
    return vec3( 2.0*(-dual[0]*real[1] + dual[1]*real[0] - dual[2]*real[3] + dual[3]*real[2]),
                 2.0*(-dual[0]*real[2] + dual[1]*real[3] + dual[2]*real[0] - dual[3]*real[1]),
                 2.0*(-dual[0]*real[3] - dual[1]*real[2] + dual[2]*real[1] + dual[3]*real[0]));
}

I solved the problem.
I feel so stupid. The code I used for store and retrieve the dual part was flawed: the quaternions are supposed to be in Wxyz form in memory!

Her’s the corrected code:

 // dual part from translation , quaternion as [x, y, z, w] w scalar part
                    dualPart[0] =  0.5f*( translation[0]*unit_quat[3] + translation[1]*unit_quat[2] - translation[2]*unit_quat[1]);
                    dualPart[1] =  0.5f*(-translation[0]*unit_quat[2] + translation[1]*unit_quat[3] + translation[2]*unit_quat[0]);
                    dualPart[2] =  0.5f*( translation[0]*unit_quat[1] - translation[1]*unit_quat[0] + translation[2]*unit_quat[3]);
                    dualPart[3] = -0.5f*( translation[0]*unit_quat[0] + translation[1]*unit_quat[1] + translation[2]*unit_quat[2]);


 vec3 dq_getTranslation( vec4 real, vec4 dual ){
    return vec3( 2.0*(-dual[3]*real[0] + dual[0]*real[3] - dual[1]*real[2] + dual[2]*real[1]),
                 2.0*(-dual[3]*real[1] + dual[0]*real[2] + dual[1]*real[3] - dual[2]*real[0]),
                 2.0*(-dual[3]*real[2] - dual[0]*real[1] + dual[1]*real[0] + dual[2]*real[3]));
}

[QUOTE=biller23;1263739]I solved the problem.
I feel so stupid. The code I used for store and retrieve the dual part was flawed: the quaternions are supposed to be in Wxyz form in memory![/QUOTE]

Good catch! Yeah, I remember hitting that too. One thread on this very issue.