了解 OpenGL 引擎的 GLTF2.0 文件的蒙皮部分

Understanding the skinning part of a GLTF2.0 file for OpenGL engine

我有一个简单的搅拌机模型,它由三个网格组成,三个骨骼分别控制一个网格。动画只是骨骼围绕 y 轴旋转立方体并返回。中心骨骼是两个外部骨骼的父骨骼。

然后我使用 GLTF2.0(文本版本)导出插件导出这个场景,现在正尝试将其导入我新制作的 opengl 引擎(c# xamarin android)。

由于想完整了解OpenGL中的GLTF2.0格式和骨骼动画,所以尝试自己实现GLTF2.0阅读

我阅读了:

显示网格很容易,但现在我无法制作动画。 在我的 gltf 文件中,我看到三种皮肤:

"skins" : [
    {
        "inverseBindMatrices" : 21,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    },
    {
        "inverseBindMatrices" : 22,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    },
    {
        "inverseBindMatrices" : 23,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    }
]

这让我很困惑,因为我对所有网格都有一个骨骼结构,而不是每个网格有三个骨骼。 我想我会在 class 个实例(比如 Bone.cs)中收集所有骨骼,每个骨骼都有一个子骨骼列表和一个父骨骼字段。然后我会在实例中收集动画 (class Animation.cs),每个动画实例都会有一个关键帧列表,其中包含给定时间戳的旋转、缩放和平移。 当动画时间戳设置为 2.5 秒时,我查找该时间戳最近的两个关键帧并为这些关键帧插入旋转、缩放和平移。

真题

希望你能帮助我。亲切的问候!! 如果需要,我可以提供更多我的代码,但我认为如果我从整体上理解了这个主题,那么我就可以自己完成了。

编辑

GLTF example model download

编辑 #2

好的,我想我正在掌握它的窍门......慢慢地。

这一切仍然笼罩着疑问。也许这对其他人有帮助。

我正在尝试一一解答您的问题

  • Why are there three skins? Why is the inverseBindMatrices bound to a skin and not to a joint?

如您所知,每个网格有一个皮肤。事实上,在您的特定情况下,您可以将所有三个网格合并为一个,并没有真正限制这个一般原则。然而

I think there needs to be an inverse bind matrix for each mesh in order to be able to transform the mesh to bone space (and - if need be - back).

每个网格每个关节都有一个逆绑定矩阵。 属性 的名称是复数形式的 inverseBindMatrices 是有原因的,它引用了 bufferview,后者又引用了 buffer.

中的一些数据

在这里更改问题的顺序,因为这样会更有意义:

Every bone node in the file has its own rotation, translation, scale values but no matrix. Why is that? Isn't there something missing?

您还需要什么?每一个仿射变换都可以分解为平移、旋转和缩放,所以数据是完整的。 glTF 规范定义结果矩阵应按 T*R*S.

的顺序计算
  • When I have the right rotation, scaling, translation from a key frame (per bone), how do I calculate the matrices for each bone that I need to pass to my vertex shader?

对于每个骨骼节点 i,您可以将局部变换计算为 M_local(i) = T(i)*R(i)*S(i)。您将通过应用完整的层次结构来获得联合矩阵,所以基本上 M_global(i) = M_global(parent(i)) * M_local(i) 然后可以将连接矩阵构造为 M_joint(i) = inverse(globalTransform) * M_global(i) * inverseBindMatrix(i).

  • The gltf file referse to a bone (joint) as a node id, but the weights/jointId-Arrays that get passed as attributes to the shader do not match these bone ids: jointIds-Array contains i.e. 0,1,2 for the bone ids, but the bones are in nodes 4,5,6 - how do I find the right bone for each jointId passed to the shader?

jointIds 数组包含对关节的引用,而不是骨骼(因此得名)。蒙皮着色器根本不关心骨骼,骨骼所做的只是在这里定义关节的层次结构,因此它们会影响 M_global 的实际值,因此也会影响 M_joint 矩阵。第 i 个条目仅引用相应皮肤的 joints 数组中的第 i 个关节,因此它需要 M_joint(i).

  • Since every Skin has a list of three (or max. 4) Joints, these are the Joints of which the final transformations need to be passed to the vertex shader.

为什么 skin 限制为 4 个关节。一个皮肤可以有任意多个关节。

If you have 8 Joints but the current to-be-drawn Mesh only gets affected by 4 of them, why should you pass all 8 matrices instead of only the 4 you need.

为什么要为只需要 4 根骨骼的网格定义 8 根骨骼的蒙皮? glTF 数据格式不会阻止您存储不相关的信息,或以低效的方式存储信息。

这里需要注意的一点是,关节之间的层级仍然由skeleton的骨骼节点层级定义。因此,您可以在单个 skin 中省略任意关节,但是这些骨骼节点(以及它们的潜在动画)仍然会影响最终的关节矩阵 - 对于由低于 [=59 的骨骼定义的任何关节=] 骨骼骨骼层次结构中的骨骼。