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

2024-02-18

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

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

由于我想完全理解GLTF2.0格式和OpenGL中的骨骼动画,所以我尝试自己实现GLTF2.0读取。

I read:

  • https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_020_Skins.md https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_020_Skins.md
  • https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/README.md https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/README.md
  • https://kcoley.github.io/glTF/specification/2.0/figures/gltfOverview-2.0.0a.png https://kcoley.github.io/glTF/specification/2.0/figures/gltfOverview-2.0.0a.png

显示网格很容易,但现在我被困在动画工作中。 在我的 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
    }
]

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

实际问题

  • 为什么会有三款皮肤?为什么 inverseBindMatrices 绑定到皮肤而不是关节?
  • 当我从关键帧(每个骨骼)进行正确的旋转、缩放、平移时,如何计算需要传递给顶点着色器的每个骨骼的矩阵?
  • 文件中的每个骨骼节点都有自己的旋转、平移、缩放值,但没有矩阵。这是为什么?是不是少了点什么?
  • gltf 文件将骨骼(关节)引用为节点 id,但作为属性传递给着色器的权重/jointId 数组与这些骨骼 id 不匹配:jointIds 数组包含骨骼,即 0,1,2 ids,但骨骼位于节点 4、5、6 中 - 如何为传递给着色器的每个 jointId 找到正确的骨骼?

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

Edit

GLTF 示例模型下载 https://www.dropbox.com/s/u8j8cf8u0qobk7u/cube_animation_test.gltf?dl=0

Edit #2

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

  • 对于由骨架控制的每个网格,文件中都有一个皮肤。我认为每个网格体都需要有一个逆绑定矩阵,以便能够将网格体转换为骨骼空间(以及 - 如果需要的话 - 返回)。

  • 我仍然不知道如何在将最终变换传递给着色器之前正确计算它们。

  • 这一点我仍然不明白。

  • 由于每个外观都有一个包含三个(或最多 4 个)关节的列表,因此这些关节的最终变换需要传递给顶点着色器。如果您有 8 个关节,但当前要绘制的网格仅受其中 4 个关节的影响,为什么要传递所有 8 个矩阵,而不是只传递您需要的 4 个矩阵。

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


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

  • 为什么会有三款皮肤?为什么 inverseBindMatrices 绑定到皮肤而不是关节?

正如您已经发现的,每个网格有一个蒙皮。事实上,在您的具体情况下,您可以将所有三个网格合并为一个,这一事实并没有真正限制这一一般原则。然而

我认为每个网格体都需要有一个逆绑定矩阵,以便能够将网格体转换为骨骼空间(以及 - 如果需要的话 - 返回)。

存在一个逆绑定矩阵每个关节 of 每个网格。该房产的名称是inverseBindMatrices出于某种原因以复数形式出现,并且它引用了bufferview这又引用了一些数据buffer.

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

文件中的每个骨骼节点都有自己的旋转、平移、缩放值,但没有矩阵。这是为什么?是不是少了点什么?

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

  • 当我从关键帧(每个骨骼)进行正确的旋转、缩放、平移时,如何计算需要传递给顶点着色器的每个骨骼的矩阵?

对于每个骨骼节点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).

  • gltf 文件将骨骼(关节)引用为节点 id,但作为属性传递给着色器的权重/jointId 数组与这些骨骼 id 不匹配:jointIds 数组包含骨骼,即 0,1,2 ids,但骨骼位于节点 4、5、6 中 - 如何为传递给着色器的每个 jointId 找到正确的骨骼?

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

  • 由于每个外观都有一个包含三个(或最多 4 个)关节的列表,因此这些关节的最终变换需要传递给顶点着色器。

为什么会有一个skin限制为 4 个关节。皮肤可以有任意数量的关节。

如果您有 8 个关节,但当前要绘制的网格仅受其中 4 个关节的影响,为什么要传递所有 8 个矩阵,而不是只传递您需要的 4 个矩阵。

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

这里需要注意的一点是,关节之间的层次结构仍然是由骨骼节点层次结构定义的。skeleton。所以你可以在一个单一的skin,但是这些骨骼节点(以及它们的潜在动画)仍然可以影响最终的关节矩阵 - 对于由骨骼定义的任何关节,该骨骼位于骨架骨骼层次结构中“遗漏”骨骼的下方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐