COLLADA:反向绑定姿势在错误的空间?

时间:2011-09-29 10:30:37

标签: c++ math matrix skinning collada

我正在编写自己的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(本地)。

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

这不使用全局轮换:

Bind pose

这是全球轮换:

Weird

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

为了进行比较,这是应该的第二个屏幕截图:

Collada viewer

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

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

enter image description here

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

world_matrix * inverse_world_matrix * position * weight

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

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

2 个答案:

答案 0 :(得分: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的版本能够正常工作,而且即使公式相同,我的版本也是如此?

这是我得到的:

Inversed!

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

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

Waaaaait一秒钟。他们上传转置?这可不容易。没办法。

enter image description here

烨。

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

未来的士兵

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

完成!

答案 1 :(得分:1)

顺便说一句,如果你在加载矩阵时转置矩阵而不是在结尾处转置矩阵(这在动画时可能会有问题)你想要以不同的方式执行乘法(上面使用的方法似乎是在DirectX中使用蒙皮)当使用OpenGL友好矩阵时 - 调整转置。)

在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);