Skin Matrix中glTF的Inverse(GlobalTransform)的含义

Meaning of glTF's Inverse(GlobalTransform) in Skin Matrix

我想用骨骼动画渲染一个网格。在制作动画之前,我只想使用动画的第一个关键帧渲染网格,即渲染具有适当骨骼层次变换的网格。我忽略了 glTF 中的场景结构;我只是使用 meshes[0] 获取网格并使用 skins[0] 获取其骨架。

我知道最终的 skin 矩阵,作为统一输入到顶点着色器,被计算

for (bone in bones) {
  bone.skin_xform = inverse(global_xform) * bone.global_xform * bone.inv_bind_xform;
}

当我这样做时,我看到我的模型在地面以下 11.4 (5.7 + 5.7) 个单位(Z = 0 处的平面;世界有 +Z 向上)。当我只渲染没有任何蒙皮的网格时,即只有位置、法线和纹理坐标时,它位于地面上。我还能够推断出为什么在蒙皮时会发生这种情况。

这是 gltf

的相关部分
    "skins" : [
        {
            "inverseBindMatrices" : 6,
            "joints" : [
                0,
            ...
        }
    ],
    "nodes" : [
        {
            "name" : "Root",
            "rotation" : [
                0,
                0,
                1,
                0
            ],
            "translation" : [
                0,
                0,
                -5.709875583648682
            ]
        },
        {
            "mesh" : 0,
            "name" : "Body",
            "skin" : 0
        },
        {
            "children" : [
                0,
                1
            ],
            "name" : "Armature",
            "translation" : [
                0,
                0,
                5.709875583648682
            ]
        }
    ]

我读过 glTF 的 documentation, tutorial and the reference guide (PDF)。虽然文档根本没有谈到它,但这里是教程和参考指南不得不说的关于 inverse(global_xform):

The vertices have to be transformed with inverse of the global transform of the node that the mesh is attached to, because this transform is already done using the model-view-matrix, and thus has to be cancelled out from the skinning computation.

据此,Bodyglobal 变换必须反转并使用。这导致 translateZ(-5.7)。 Root 已经有了 translateZ(-5.7) 的局部变换,所以我理解网格到地面的 -11.4 偏移量。但是,如果我按原样使用 Body 的全局变换,没有反转,在上面的公式中就没有问题。

为什么参考指南要求我们反转根骨骼父级的全局变换?我错过了什么?当我从 Blender 导入这个模型时,我注意到 armature 对象上的变换确实是 translateZ(5.7).

你说

I'm ignoring the scene structure in the glTF; I'm just using meshes[0] to get the mesh and skins[0] to get its skeleton.

但是,(相关部分)标准说(强调我的)

The vertices have to be transformed with inverse of the global transform of the node that the mesh is attached to, because this transform is already done using the model-view-matrix

既然你说你在没有场景结构的情况下从 glTF 中提取网格和骨架,那么就不需要 inverse(global_xform)。这是因为您没有针对 inverse(global_xform) 的网格 model-view-matrix 的 non-identity 变换来抵消偏移。 three.js 一切正常,因为它渲染了整个场景及其所有节点层次结构,这与您的不同。

However, if I use Body's global transform as-is, without inversion, in the above formula there're no issues.

这是正确的用法,因为 Root 的全局变换是其所有 parent 变换与 Root 的局部变换

的串联
root.global_xform = armature.local_xform * body.local_xform * root.local_xform

根据您的评论,我看到电枢 object 有一个 non-identity 变换作为其位置。通常作为身份更好; reference: a tutorial on skeletal animation by TheThinMatrix.


这里有更详细的计算说明。我们看到 Root 节点有一个 TranslateZ(-5.7) 的局部变换;它的 parent,即 Armature 节点,具有 TranslateZ(5.7) 的局部变换;电枢没有进一步的 parents。因此 Root 的全局变换实际上是恒等式。这是蒙皮顶点着色器中的等式

point’ = P * V * Mesh *              Skin                  * point
point’ = P * V * Mesh * (InvMeshGlobal * Global * InvBind) * point
                  5.7 * (-5.7 * 5.7 * -5.7 * InvBind)
                  5.7 * (-5.7 * I * InvBind)

因此只有在渲染整个场景层次结构时才需要 InvGlobalXform(上面写为 InvMeshGlobal),而您只是获得了网格及其骨架,忽略了节点之外的祖先节点 meshskin 存在。我可以想出两个解决方案。

解决方案 1

  • 确保网格和骨架应用了 LocationRotationScale导出前的搅拌器
  • Root 和它的 parent Armature 本地变换都将成为恒等式,即根的全局变换将是恒等式
  • 忽略 MeshInvMeshGlobal 转换
  • 整个等式只需要InvBind

解决方案 2

  • 存储Root的祖先变换
  • 使用祖先变换得到 Root 的正确全局变换;通常成为身份
  • 忽略 MeshInvMeshGlobal 转换
  • 等式中需要
  • GlobalInvBind

解决方案(1)仅在输入网格和骨架没有全局旋转或平移时有效;解决方案 (2) 也适用于此。与解决方案 (1) 不同的是,解决方案 (2) 需要存储祖先变换。