-1
\$\begingroup\$

I'm trying to convert my code to use dual quaternion skinning instead of matrix skinning because i just can't get the skinning matrix created correctly from bones+weights using matrices.

Edit: just to clarify, if each vert has only one bone the matrix version did skin correctly.

But it's just not working and i really don't understand why, i'm using code i found here:

http://www.chinedufn.com/dual-quaternion-shader-explained/

http://donw.io/post/dual-quaternion-skinning/

I've looked at the assorted papers often brought up in the answers to these questions, but for the most part i just don't understand them. I'm not even entirely sure how the [x, y, z, w] of a glm::quat correspond to the [1, i, j, k] of a quaternion--I think that w might be the scalar because it goes first in the actual byte order of the glm::quat struct, but i'm not sure.

Anyway, in my shader bindings i have:

void gltfShader::bindBones(const std::vector<glm::mat4> & bones)
{
    std::vector<glm::fdualquat> dual_quats(std::min<int>(bones.size(), 64));

    glm::quat r;
    glm::vec3 t, s, sk;
    glm::vec4 pr;
    glm::fdualquat dq;

    for(size_t i = 0; i < dual_quats.size(); ++i)
    {
        glm::decompose(bones[i], s, r, t, sk, pr);

        dq[0] = r;
        dq[1] = glm::quat(t.x, t.y, t.z, 0) * r * .5f;

        dual_quats[i] = dq;
    }

    glUniformMatrix2x4fv(u_bones, dual_quats.size(), GL_FALSE, (float*) &dual_quats[0]);
}

In the vertex shader:

mat2x4 GetBoneTransform(ivec4 joints, vec4 weights)
{
    float sum_weight = weights.x + weights.y + weights.z + weights.w;

    // Fetch bones
    mat2x4 dq0 = u_bones[joints.x];
    mat2x4 dq1 = u_bones[joints.y];
    mat2x4 dq2 = u_bones[joints.z];
    mat2x4 dq3 = u_bones[joints.w];

    // Ensure all bone transforms are in the same neighbourhood
    weights.y *= sign(dot(dq0[0], dq1[0]));
    weights.z *= sign(dot(dq0[0], dq2[0]));
    weights.w *= sign(dot(dq0[0], dq3[0]));

    // Blend
    mat2x4 result =
        weights.x * dq0 +
        weights.y * dq1 +
        weights.z * dq2 +
        weights.w * dq3;

    result[0][3] += int(sum_weight < 1) * (1 - sum_weight);

    // Normalise
    float norm = length(result[0]);
    return result / norm;
}


mat4 GetSkinMatrix()
{
    mat2x4 bone = GetBoneTransform(a_joints0, a_weights0);

    vec4 r = bone[0];
    vec4 t = bone[1];

    return mat4(
        1.0 - (2.0 * r.y * r.y) - (2.0 * r.z * r.z),
              (2.0 * r.x * r.y) + (2.0 * r.w * r.z),
              (2.0 * r.x * r.z) - (2.0 * r.w * r.y),
        0.0,

              (2.0 * r.x * r.y) - (2.0 * r.w * r.z),
        1.0 - (2.0 * r.x * r.x) - (2.0 * r.z * r.z),
              (2.0 * r.y * r.z) + (2.0 * r.w * r.x),
        0.0,

              (2.0 * r.x * r.z) + (2.0 * r.w * r.y),
              (2.0 * r.y * r.z) - (2.0 * r.w * r.x),
        1.0 - (2.0 * r.x * r.x) - (2.0 * r.y * r.y),
        0.0,

        2.0 * (-t.w * r.x + t.x * r.w - t.y * r.z + t.z * r.y),
        2.0 * (-t.w * r.y + t.x * r.z + t.y * r.w - t.z * r.x),
        2.0 * (-t.w * r.z - t.x * r.y + t.y * r.x + t.z * r.w),
        1);
}

EDIT: video of the results:

https://youtu.be/8jIt_Xhhffk

The one on the left is supposed to be a walk cycle, the one on the right is supposed to be this:

https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Monster

\$\endgroup\$
  • 3
    \$\begingroup\$ "it's just not working" - what are the patient's symptoms? Are you seeing a different skinning transformation than you expect? In what way? (Pairs of images showing expected / observed behaviour can help here) Is no skinning happening at all? Do you get a runtime or compilation error? All these different symptoms can help narrow down the root cause, so give us as much information as you can. \$\endgroup\$ – DMGregory Oct 10 '18 at 14:08
  • \$\begingroup\$ Edited, video added. \$\endgroup\$ – Patrick Jeeves Oct 10 '18 at 14:32
  • \$\begingroup\$ Standard debug steps: does identity quaternion work? Does just translation work? Does pure rotation work? \$\endgroup\$ – ratchet freak Oct 10 '18 at 14:44
  • \$\begingroup\$ identity works, just translation and just rotation both produce odd results. Using the first bone indexed instead of combining is the same with sharper corners, i think the monster might have more problems because it's scale decomposition isn't the identity vector. \$\endgroup\$ – Patrick Jeeves Oct 10 '18 at 14:57
0
\$\begingroup\$

changing this:

for(size_t i = 0; i < dual_quats.size(); ++i)
{
    glm::decompose(bones[i], s, r, t, sk, pr);

    dq[0] = r;
    dq[1] = glm::quat(t.x, t.y, t.z, 0) * r * .5f;
//this produces worse results:
//   dq[1] = glm::quat(0, t.x, t.y, t.z) * r * .5f;

    dual_quats[i] = dq;
}

to this:

for(size_t i = 0; i < dual_quats.size(); ++i)
{
    r = glm::quat_cast(bones[i]);
    t = glm::vec3(bones[i][3]);

    dq[0] = r;
    dq[1] = glm::quat(0, t.x, t.y, t.z) * r * .5f;

    dual_quats[i] = dq;
}

fixed it. I really have no idea why.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.