13

我正在编写自己的 COLLADA 导入器。我已经走了很远,加载网格和材料等。但我在动画方面遇到了障碍,特别是:关节旋转。

我用于为网格蒙皮的公式很简单:

weighted;
for (i = 0; i < joint_influences; i++)
{
    weighted += 
        joint[joint_index[i]]->parent->local_matrix * 
        joint[joint_index[i]]->local_matrix * 
        skin->inverse_bind_pose[joint_index[i]] * 
        position * 
        skin->weight[j];
}
position = weighted;

就文献而言,这是正确的公式。现在,COLLADA 为关节指定了两种类型的旋转:局部和全局。您必须将旋转连接在一起以获得关节的局部变换。

COLLADA 文档没有区分关节的局部旋转和关节的全局旋转。但在我见过的大多数模型中,旋转的 id 可以是rotate(全局)或jointOrient(局部)。

当我忽略全局旋转而只使用局部旋转时,我得到了模型的绑定姿势。但是当我将全局旋转添加到关节的局部变换中时,奇怪的事情开始发生。

这是不使用全局旋转:

绑定姿势

这是全局旋转:

奇怪的

在这两个屏幕截图中,我都使用线条来绘制骨架,但在第一个屏幕中它是不可见的,因为关节位于网格内部。在第二个屏幕截图中,顶点到处都是!

为了比较,这是第二个屏幕截图的样子

科拉达查看器

很难看到,但您可以在第二个屏幕截图中看到关节处于正确位置。

但现在奇怪的事情。如果我忽略 COLLADA 指定的反向绑定姿势,而是取关节的父局部变换乘以关节的局部变换的倒数,我得到以下结果:

在此处输入图像描述

在这个屏幕截图中,我从每个顶点到有影响的关节画了一条线。我得到绑定姿势的事实并不奇怪,因为现在公式变成了:

world_matrix * inverse_world_matrix * position * weight

但这让我怀疑 COLLADA 的反向绑定姿势在错误的空间中。

所以我的问题是:COLLADA 在什么空间指定它的反向绑定姿势?以及如何将反向绑定姿势转换为我需要的空间?

4

2 回答 2

12

我首先将我的值与我从 Assimp(一个开源模型加载器)读取的值进行比较。通过代码,我查看了他们构建绑定矩阵和逆绑定矩阵的位置。

最终我最终进入SceneAnimator::GetBoneMatrices,其中包含以下内容:

// 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始终是身份,因为网格不会改变任何东西。currentGlobalTransform是绑定矩阵,关节的父节点的局部矩阵与关节的局部矩阵连接。是逆绑定矩阵,mOffsetMatrix它直接来自皮肤。

我将这些矩阵的值与我自己的值进行了对比(哦,是的,我在观察窗口中比较了它们),它们完全一样,相差可能 0.0001%,但这微不足道。那么为什么 Assimp 的版本可以工作,而我的版本没有,即使公式相同?

这是我得到的:

反转!

当 Assimp 最终将矩阵上传到蒙皮着色器时,它们会执行以下操作:

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

等一下。他们上传它们转置?不可能那么容易。没门。

在此处输入图像描述

是的。

我做错了其他事情:在应用蒙皮矩阵之前,我将坐标转换为正确的系统(厘米到米)。这会导致模型完全失真,因为矩阵是为原始坐标系设计的。

未来的谷歌用户

  • 按照接收顺序读取所有节点变换(旋转、平移、缩放等)。
  • 将它们连接到关节的局部矩阵
  • 取关节的父节点并将其与局部矩阵相乘。
  • 将其存储为绑定矩阵
  • 阅读皮肤信息。
  • 存储关节的反向绑定姿势矩阵
  • 存储每个顶点的联合权重。
  • 绑定矩阵逆绑定姿势矩阵相乘转置,称为蒙皮矩阵
  • 蒙皮矩阵乘以位置乘以关节权重,并将其添加到加权位置
  • 使用加权位置进行渲染。

完毕!

于 2011-10-02T06:28:44.687 回答
1

顺便说一句,如果您在加载矩阵时转置矩阵而不是在最后转置矩阵(在制作动画时可能会出现问题),您希望以不同的方式执行乘法(您上面使用的方法似乎是在使用 OpenGL 时在 DirectX 中使用蒙皮友好矩阵 - 因此转置。)

在 DirectX 中,我在从文件加载矩阵时转置矩阵,然后使用(在下面的示例中,为了简单起见,我只是应用绑定姿势):

XMMATRIX l_oWorldMatrix = XMMatrixMultiply( l_oBindPose, in_oParentWorldMatrix );

XMMATRIX l_oMatrixPallette = XMMatrixMultiply( l_oInverseBindPose, l_oWorldMatrix );

XMMATRIX l_oFinalMatrix = XMMatrixMultiply( l_oBindShapeMatrix, l_oMatrixPallette );

于 2013-05-15T22:58:15.980 回答