COLLADA:逆绑定姿势在错误的空间中?

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文档没有区分关节的本地旋转和全局旋转。但在我看到的大多数模型中,旋转可以具有rotate(全局)或jointOrient(本地)的id。
当我忽略全局旋转,仅使用本地旋转时,我会得到模型的绑定姿势。但是,当我将全局旋转添加到关节的本地变换中时,奇怪的事情开始发生。
这是不使用全局旋转的情况:

Bind pose

这是使用全局旋转的效果:

Weird

在这两个截图中,我使用线条来绘制骨架,但在第一个截图中,它是不可见的,因为关节在网格内部。在第二个截图中,顶点分散无序!

为了比较,这才是第二个截图应该呈现的样子:

Collada viewer

虽然不易看出,但第二张截图中可以看到关节处于正确的位置。

但现在有个奇怪的事情。如果我忽略COLLADA指定的反向绑定姿势,而是采用关节父级本地变换的逆乘以关节本地变换的方法,我得到以下结果:

enter image description here

在这张截图中,我正在从每个顶点绘制一条线到具有影响力的关节。我得到绑定姿势并不奇怪,因为公式现在变为:
world_matrix * inverse_world_matrix * position * weight

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

所以我的问题是:COLLADA 指定其反向绑定姿势处于什么空间?如何将反向绑定姿势转换到我需要的空间?


你有读过 COLLADA 1.4.1 规范中的“在 COLLADA 中绑定骨架”一节吗?你的公式似乎有误。 - jterrace
2个回答

12

我开始通过将我的值与我从Assimp(一个开源的模型加载器)读取的值进行比较。在代码中单步调试时,我查看了它们构建绑定矩阵及其逆绑定矩阵的位置。

最终,我来到了 SceneAnimator :: GetBoneMatrices ,其中包含以下内容:

<code>// 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;
}
</code>
globalInverseMeshTransform 始终是单位矩阵,因为网格没有进行任何转换。currentGlobalTransform 是绑定矩阵,即关节父级的本地矩阵与关节本地矩阵连接起来得到的结果。mOffsetMatrix 是反向绑定矩阵,直接来自皮肤。

我将这些矩阵的值与我的值进行了比较(是的,我在观察窗口中进行了比较),它们完全相同,可能会有0.0001%的差异,但这是不重要的。 那么为什么Assimp的版本可以工作,而我的版本却不能,即使公式是相同的?

这是我得到的结果:

Inversed!

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

helper->piEffect->SetMatrixTransposeArray( "gBoneMatrix", (D3DXMATRIX*)matrices, 60);
等等,他们上传的时候是“转置”的?这不能这么简单吧。不可能。 enter image description here 没错。
我还做错了其他事情:在应用蒙皮矩阵之前,我将坐标转换为正确的系统(厘米到米)。这会导致完全扭曲的模型,因为矩阵是针对原始坐标系设计的。
未来的谷歌搜索者:
- 按接收顺序读取所有节点变换(旋转、平移、缩放等)。 - 将它们连接到关节的本地矩阵上。 - 取关节的父级并将其与局部矩阵相乘。 - 将其存储为绑定矩阵。 - 读取皮肤信息。 - 存储关节的逆绑定姿势矩阵。 - 存储每个顶点的关节权重。 - 将绑定矩阵与逆绑定姿势矩阵相乘,并进行转置,称之为蒙皮矩阵。 - 将蒙皮矩阵与位置乘以关节权重,并加入到加权位置中。 - 使用加权位置进行渲染。
完成!

1
老问题,但非常有帮助!非常感谢! - Jas

1
BTW,如果您在加载矩阵时转置它们而不是在最后转置矩阵(这在动画时可能会有问题),则要以不同的方式执行乘法(您上面使用的方法似乎是在使用 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接