COLLADA: Inverse bind pose in the wrong space?

后端 未结 2 1936
無奈伤痛
無奈伤痛 2021-02-05 15:31

I\'m working on writing my own COLLADA importer. I\'ve gotten pretty far, loading meshes and materials and such. But I\'ve hit a snag on animation, specifically: joint rotations

相关标签:
2条回答
  • 2021-02-05 16:18

    I started by comparing my values to the ones I read from Assimp (an open source model loader). Stepping through the code I looked at where they built their bind matrices and their inverse bind matrices.

    Eventually I ended up in SceneAnimator::GetBoneMatrices, which contains the following:

    // Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose
    // Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform
    for( size_t a = 0; a < mesh->mNumBones; ++a)
    {
        const aiBone* bone = mesh->mBones[a];
        const aiMatrix4x4& currentGlobalTransform
            = GetGlobalTransform( mBoneNodesByName[ bone->mName.data ]);
        mTransforms[a] = globalInverseMeshTransform * currentGlobalTransform * bone->mOffsetMatrix;
    }
    

    globalInverseMeshTransform is always identity, because the mesh doesn't transform anything. currentGlobalTransform is the bind matrix, the joint's parent's local matrices concatenated with the joint's local matrix. And mOffsetMatrix is the inverse bind matrix, which comes directly from the skin.

    I checked the values of these matrices to my own (oh yes I compared them in a watch window) and they were exactly the same, off by maybe 0.0001% but that's insignificant. So why does Assimp's version work and mine doesn't even though the formula is the same?

    Here's what I got:

    Inversed!

    When Assimp finally uploads the matrices to the skinning shader, they do the following:

    helper->piEffect->SetMatrixTransposeArray( "gBoneMatrix", (D3DXMATRIX*)matrices, 60);
    

    Waaaaait a second. They upload them transposed? It couldn't be that easy. No way.

    enter image description here

    Yup.

    Something else I was doing wrong: I was converting the coordinates the right system (centimeters to meters) before applying the skinning matrices. That results in completely distorted models, because the matrices are designed for the original coordinate system.

    FUTURE GOOGLERS

    • Read all the node transforms (rotate, translation, scale, etc.) in the order you receive them.
    • Concatenate them to a joint's local matrix.
    • Take the joint's parent and multiply it with the local matrix.
    • Store that as the bind matrix.
    • Read the skin information.
    • Store the joint's inverse bind pose matrix.
    • Store the joint weights for each vertex.
    • Multiply the bind matrix with the inverse bind pose matrix and transpose it, call it the skinning matrix.
    • Multiply the skinning matrix with the position times the joint weight and add it to the weighted position.
    • Use the weighted position to render.

    Done!

    0 讨论(0)
  • 2021-02-05 16:25

    BTW, if you transpose the matrices upon loading them rather than transposing the matrix at the end (which can be problematic when animating) you want to perform your multiplication differently (the method you use above appears to be for using skinning in DirectX when using OpenGL friendly matrices - ergo the transpose.)

    In DirectX I transpose matrices when they are loaded from the file and then I use (in the example below I am simply applying the bind pose for the sake of simplicity):

    XMMATRIX l_oWorldMatrix = XMMatrixMultiply( l_oBindPose, in_oParentWorldMatrix );

    XMMATRIX l_oMatrixPallette = XMMatrixMultiply( l_oInverseBindPose, l_oWorldMatrix );

    XMMATRIX l_oFinalMatrix = XMMatrixMultiply( l_oBindShapeMatrix, l_oMatrixPallette );

    0 讨论(0)
提交回复
热议问题