Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 5 of 5

Thread: Wrong Blending of 4 Dual Quaternions

  1. #1
    Junior Member Newbie
    Join Date
    Sep 2011
    Location
    Florence, Italy
    Posts
    18

    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:

    Code :
                    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 ):

    Code :
                    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.

  2. #2
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    4,124
    Quote Originally Posted by biller23 View Post
    Code glsl:
                    float invLen = inversesqrt ( dot(blendedDQ[0], blendedDQ[0] );
                    mat3 skinRot =  invLen * q_getMat3( blendedDQ[0] );
                    vec3 skinTra =  invLen * dq_getTranslation( blendedDQ[0], blendedDQ[1] );
    Your normalizing should be applied to the dual quats themselves. Try:

    Code glsl:
                    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] );

  3. #3
    Junior Member Newbie
    Join Date
    Sep 2011
    Location
    Florence, Italy
    Posts
    18
    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:

    Code :
    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;
    }


    Code :
    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:
    Code :
                        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:
    Code :
    // 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]));
    }

  4. #4
    Junior Member Newbie
    Join Date
    Sep 2011
    Location
    Florence, Italy
    Posts
    18
    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:

    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]);

    Code :
     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]));
    }

  5. #5
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    4,124
    Quote Originally Posted by biller23 View Post
    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!
    Good catch! Yeah, I remember hitting that too. One thread on this very issue.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •