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
(本地)。
当我忽略全局旋转而仅使用局部旋转时,我得到了模型的绑定姿势。但是当我将全局旋转添加到关节的局部变换中时,奇怪的事情开始发生。
这是没有使用全局旋转的情况:
这是使用全局旋转的情况:
在两个屏幕截图中,我都使用线条绘制骨架,但在第一个屏幕截图中它是不可见的,因为关节位于网格内部。在第二个屏幕截图中,顶点遍布各处!
为了进行比较,第二个屏幕截图应该如下所示:
很难看到,但您可以在第二个屏幕截图中看到关节处于正确的位置。
但现在奇怪的是。如果我忽略 COLLADA 指定的反向绑定姿势,而是取关节父局部变换乘以关节局部变换的倒数,我会得到以下结果:
在此屏幕截图中,我从每个顶点到有影响的关节绘制了一条线。我得到绑定姿势的事实并不那么奇怪,因为公式现在变成了:
world_matrix * inverse_world_matrix * position * weight
但它让我怀疑 COLLADA 的逆绑定姿势位于错误的空间中。
所以我的问题是:COLLADA 在什么空间指定其逆绑定姿势?如何将反向绑定姿势转换为我需要的空间?
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.
The formula I'm using for skinning my meshes is straight-forward:
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;
And as far as the literature is concerned, this is the correct formula. Now, COLLADA specifies two types of rotations for the joints: local and global. You have to concatenate the rotations together to get the local transformation for the joint.
What the COLLADA documentation does not differentiate between is the joint's local rotation and the joint's global rotation. But in most of the models I've seen, rotations can have an id of either rotate
(global) or jointOrient
(local).
When I disregard the global rotations and only use the local ones, I get the bind pose for the model. But when I add the global rotations to the joint's local transformation, strange things start to happen.
This is without using global rotations:
And this is with global rotations:
In both screenshots I'm drawing the skeleton using lines, but in the first it's invisible because the joints are inside the mesh. In the second screenshot the vertices are all over the place!
For comparison, this is what the second screenshot should look like:
It's hard to see, but you can see that the joints are in the correct position in the second screenshot.
But now the weird thing. If I disregard the inverse bind pose as specified by COLLADA and instead take the inverse of the joint's parent local transform times the joint's local transform, I get the following:
In this screenshot I'm drawing a line from each vertex to the joints that have influence. The fact that I get the bind pose is not so strange, because the formula now becomes:
world_matrix * inverse_world_matrix * position * weight
But it leads me to suspect that COLLADA's inverse bind pose is in the wrong space.
So my question is: in what space does COLLADA specifies its inverse bind pose? And how can I transform the inverse bind pose to the space I need?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我首先将我的值与我从 Assimp(开源模型加载器)读取的值进行比较。通过代码,我查看了他们构建绑定矩阵和逆绑定矩阵的位置。
最终,我最终得到了
SceneAnimator::GetBoneMatrices
,其中包含以下内容:globalInverseMeshTransform
始终是恒等的,因为网格不会转换任何内容。 currentGlobalTransform 是绑定矩阵,即关节父级的局部矩阵与关节的局部矩阵相连接。而mOffsetMatrix
是逆绑定矩阵,它直接来自于皮肤。我将这些矩阵的值与我自己的值进行了检查(哦,是的,我在观察窗口中比较了它们),它们完全相同,可能相差 0.0001%,但这微不足道。那么为什么 Assimp 的版本可以工作而我的却不能,尽管公式是一样的呢?
这是我得到的结果:
当 Assimp 最终将矩阵上传到蒙皮着色器时,它们会执行以下操作:
Waaaaait 一秒钟。他们上传它们转置?事情不可能那么容易。决不。
是的。
我还做错了一些事情:在应用蒙皮矩阵之前,我将坐标转换为正确的系统(厘米到米)。这会导致模型完全扭曲,因为矩阵是为原始坐标系设计的。
未来的谷歌浏览器
完毕!
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: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. AndmOffsetMatrix
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:
When Assimp finally uploads the matrices to the skinning shader, they do the following:
Waaaaait a second. They upload them transposed? It couldn't be that easy. No way.
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
Done!
顺便说一句,如果您在加载矩阵时转置它们而不是在最后转置矩阵(这在动画时可能会出现问题),您希望以不同的方式执行乘法(上面使用的方法似乎是在使用 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 );
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 );