Search code examples
matrixinterpolationquaternions

Interpolation Between 2 4x4 Matrices


For skeletal animation using colladas, I need to linearly interpolate between 2 matrices. I saw somewhere that I can use quaternions to interpolate between matrices, but that only works for the rotational component, and I need to preserve the transform as well. Here is my code, which works, except for the translation part:

float total = (orderedBones[i]->Animation->keyFrames[nextKeyFrame] - orderedBones[i]->Animation->keyFrames[nextKeyFrame - 1]) * 100.0;
float progress = orderedBones[i]->Animation->accumTime - orderedBones[i]->Animation->keyFrames[nextKeyFrame - 1] * 100.0;
float interpolation = progress / total;

glm::quat firstQuat = glm::quat_cast(orderedBones[i]->Animation->Matrices[nextKeyFrame - 1]);
glm::quat secondQuat = glm::quat_cast(orderedBones[i]->Animation->Matrices[nextKeyFrame]);
glm::quat finalQuat = glm::slerp(firstQuat, secondQuat, interpolation);

orderedBones[i]->Animation->interpoltaedMatrix = glm::mat4_cast(finalQuat);

Is there any way that I can do this?


Solution

  • I ended up solving my question through a bit more web surfing. For future reference, heres how to do it.

    The transformation component is stored in a 4x4 matrix like this:

    r r r t
    r r r t
    r r r t
    0 0 0 1
    

    where r is the rotational component and t is the translation component. Because of this, we can represent the translation component as a vector. 2 Vectors can be linearly interpolated, so we interpolate those two vectors and then shove them back into the rotation matrix when they're done. Heres the final code, but its a bit messy:

    float total = (orderedBones[i]->Animation->keyFrames[nextKeyFrame] - orderedBones[i]->Animation->keyFrames[nextKeyFrame - 1]) * ANIMATION_MULTIPLICATION_CONST;
    float progress = orderedBones[i]->Animation->accumTime - orderedBones[i]->Animation->keyFrames[nextKeyFrame - 1] * ANIMATION_MULTIPLICATION_CONST;
    float interpolation = progress / total;
    
    glm::quat firstQuat = glm::quat_cast(orderedBones[i]->Animation->Matrices[nextKeyFrame - 1]);
    glm::quat secondQuat = glm::quat_cast(orderedBones[i]->Animation->Matrices[nextKeyFrame]);
    glm::quat finalQuat = glm::slerp(firstQuat, secondQuat, interpolation);
    
    orderedBones[i]->Animation->interpoltaedMatrix = glm::mat4_cast(finalQuat);
    
    glm::vec4 transformComp1 = glm::vec4(
        orderedBones[i]->Animation->Matrices[nextKeyFrame - 1][0][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame - 1][1][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame - 1][2][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame - 1][3][3]);
    glm::vec4 transformComp2 = glm::vec4(
        orderedBones[i]->Animation->Matrices[nextKeyFrame][0][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame][1][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame][2][3],
        orderedBones[i]->Animation->Matrices[nextKeyFrame][3][3]);
    
    glm::vec4 finalTrans = (float)(1.0 - interpolation) * transformComp1 + transformComp2 * interpolation;
    
    // good for now, although in future the 2 transformation components need to be interpolated
    orderedBones[i]->Animation->interpoltaedMatrix[0][3] = finalTrans.x;
    orderedBones[i]->Animation->interpoltaedMatrix[1][3] = finalTrans.y;
    orderedBones[i]->Animation->interpoltaedMatrix[2][3] = finalTrans.z;
    orderedBones[i]->Animation->interpoltaedMatrix[3][3] = finalTrans.w;
    

    Hope that answers anybody else's questions :)