0

我想用骨骼动画渲染一个网格。在制作动画之前,我只想用动画的第一个关键帧渲染网格,即渲染网格与骨骼层次变换到位。我忽略了 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 的文档教程参考指南 (PDF)。虽然文档根本没有谈到它,但教程和参考指南不得不说inverse(global_xform)

顶点必须使用网格附加到的节点的全局变换的进行变换,因为该变换已经使用模型-视图-矩阵完成,因此必须从蒙皮计算中取消。

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

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

4

1 回答 1

0

你说

我忽略了 glTF 中的场景结构;我只是使用 meshes[0] 来获取网格和 skins[0] 来获取它的骨架。

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

顶点必须使用网格附加到的节点的全局变换的逆进行变换,因为该变换已经使用模型-视图-矩阵完成

既然你说你是从 glTF 中取出网格和骨架而没有场景结构inverse(global_xform)是不必要的。这是因为您的网格没有model-view-matrix用于inverse(global_xform)抵消偏移的非恒等变换。Three.js 工作正常,因为它渲染了具有所有节点层次结构的整个场景,与您的不同。

但是,如果我按原样使用 Body 的全局变换,没有反转,在上面的公式中就没有问题。

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

root.global_xform = armature.local_xform * body.local_xform * root.local_xform

根据您的评论,我看到电枢对象具有非身份变换作为其位置。通常作为身份更好;参考:TheThinMatrix 的骨骼动画教程


这是更详细的计算说明。我们看到该Root节点的局部变换为TranslateZ(-5.7); 它的父Armature节点,节点,有一个局部变换TranslateZ(5.7);armature 没有其他父母。因此,全局变换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

  • 确保网格和骨架在导出之前在 Blender 中应用了LocationRotationScale
  • 两者Root及其父Armature本地变换都将成为身份,即根的全局变换将成为身份
  • 忽略两者MeshInvMeshGlobal转换
  • 整个方程式中只InvBind需要

解决方案 2

  • StoreRoot的祖传变换
  • 使用祖先变换来达到Root' 正确的全局变换;通常成为身份
  • 忽略两者MeshInvMeshGlobal转换
  • Global并且InvBind需要在方程中

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

于 2021-05-04T08:39:28.537 回答