问题描述
我想用骨架动画渲染网格。在制作动画之前,我只想使用动画的第一个关键帧渲染网格,即渲染具有适当骨骼层次变换的网格。我忽略了 glTF 中的场景结构;我只是使用 meshes[0]
获取网格并使用 skins[0]
获取其骨架。
我知道最终的 skin
矩阵是作为uniforms 提供给顶点着色器的计算
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,1,0
],"translation" : [
0,-5.709875583648682
]
},{
"mesh" : 0,"name" : "Body","skin" : 0
},{
"children" : [
0,1
],"name" : "Armature",5.709875583648682
]
}
]
我读过 glTF 的 documentation、tutorial 和 reference guide (PDF)。虽然文档根本没有提及它,但以下是教程和参考指南对 inverse(global_xform)
的说明:
顶点必须使用网格所附加节点的全局变换的逆进行变换,因为这种变换已经使用模型-视图-矩阵完成,因此必须从蒙皮计算中取消。
据此,Body
的 global 转换必须反转并使用。这导致 translateZ(-5.7)
。 Root 已经有 translateZ(-5.7)
的局部变换,所以我理解网格到地面的 -11.4 偏移。但是,如果我按原样使用 Body
的全局变换,没有反转,则在上述公式中没有问题。
为什么参考指南要求我们反转根骨骼父级的全局变换?我错过了什么?当我从 Blender 导入这个模型时,我注意到 armature 对象上的变换确实是 translateZ(5.7)
。
解决方法
你说
我忽略了 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
根据您的评论,我看到骨架对象的位置具有非同一性变换。通常作为身份更好; reference: a tutorial on skeletal animation by TheThinMatrix。
这里有更详细的计算说明。我们看到 Root
节点具有 TranslateZ(-5.7)
的局部变换;它的父节点,Armature
节点,具有 TranslateZ(5.7)
的局部变换;骨架没有其他父母。因此 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
)仅在渲染整个场景层次结构时才需要,而您刚刚获得网格及其骨架,忽略节点之外的祖先节点 {{1 }} 和 mesh
存在。我可以想出两个解决方案。
解决方案 1
- 在导出之前,确保网格和骨架在 Blender 中应用了 Location、Rotation 和 Scale
-
skin
及其父Root
本地变换都将成为身份,即 root 的全局变换将成为身份 - 忽略
Armature
和Mesh
转换 - 整个等式只需要
InvMeshGlobal
解决方案 2
- 存储
InvBind
的祖先变换 - 使用祖先变换来达到
Root
的适当全局变换;通常成为身份 - 忽略
Root
和Mesh
转换 -
InvMeshGlobal
和Global
需要在等式中
解决方案(1)仅在输入网格和骨架没有全局旋转或平移时有效;解决方案 (2) 也适用于此。与解决方案(1)不同,解决方案(2)需要存储祖先变换。