了解 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阅读
我阅读了:
- 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://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
}
]
这让我很困惑,因为我对所有网格都有一个骨骼结构,而不是每个网格有三个骨骼。
我想我会在 class 个实例(比如 Bone.cs)中收集所有骨骼,每个骨骼都有一个子骨骼列表和一个父骨骼字段。然后我会在实例中收集动画 (class Animation.cs),每个动画实例都会有一个关键帧列表,其中包含给定时间戳的旋转、缩放和平移。
当动画时间戳设置为 2.5 秒时,我查找该时间戳最近的两个关键帧并为这些关键帧插入旋转、缩放和平移。
真题
- 为什么是三个皮肤?为什么 inverseBindMatrices 绑定到皮肤而不是关节?
- 当我从关键帧(每个骨骼)进行正确的旋转、缩放、平移时,如何计算我需要传递给顶点着色器的每个骨骼的矩阵?
- 文件中的每个骨骼节点都有自己的旋转、平移、缩放值但没有矩阵。这是为什么?是不是少了什么?
- gltf 文件引用骨骼(关节)作为节点 ID,但作为属性传递给着色器的 weights/jointId-Arrays 与这些骨骼 ID 不匹配:jointIds-Array 包含即 0,1 ,2 表示骨骼 ID,但骨骼位于节点 4、5、6 - 如何为传递给着色器的每个 jointId 找到正确的骨骼?
希望你能帮助我。亲切的问候!!
如果需要,我可以提供更多我的代码,但我认为如果我从整体上理解了这个主题,那么我就可以自己完成了。
编辑
编辑 #2
好的,我想我正在掌握它的窍门......慢慢地。
对于每个由骨架控制的Mesh,文件中都有一个Skin。我认为每个网格都需要一个反向绑定矩阵,以便能够将网格转换为骨骼 space(并且 - 如果需要 - 返回)。
我仍然不知道如何在将它们传递给着色器之前正确计算最终变换。
这一点我还是不懂。
由于每个皮肤都有一个包含三个(或最多 4 个)关节的列表,因此这些是最终转换需要传递给顶点着色器的关节。如果你有 8 个关节,但当前要绘制的网格仅受其中 4 个的影响,为什么你应该传递所有 8 个矩阵而不是只传递你需要的 4 个。
这一切仍然笼罩着疑问。也许这对其他人有帮助。
我正在尝试一一解答您的问题
- 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 的骨骼定义的任何关节=] 骨骼骨骼层次结构中的骨骼。
我有一个简单的搅拌机模型,它由三个网格组成,三个骨骼分别控制一个网格。动画只是骨骼围绕 y 轴旋转立方体并返回。中心骨骼是两个外部骨骼的父骨骼。
然后我使用 GLTF2.0(文本版本)导出插件导出这个场景,现在正尝试将其导入我新制作的 opengl 引擎(c# xamarin android)。
由于想完整了解OpenGL中的GLTF2.0格式和骨骼动画,所以尝试自己实现GLTF2.0阅读
我阅读了:
- 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://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
}
]
这让我很困惑,因为我对所有网格都有一个骨骼结构,而不是每个网格有三个骨骼。 我想我会在 class 个实例(比如 Bone.cs)中收集所有骨骼,每个骨骼都有一个子骨骼列表和一个父骨骼字段。然后我会在实例中收集动画 (class Animation.cs),每个动画实例都会有一个关键帧列表,其中包含给定时间戳的旋转、缩放和平移。 当动画时间戳设置为 2.5 秒时,我查找该时间戳最近的两个关键帧并为这些关键帧插入旋转、缩放和平移。
真题
- 为什么是三个皮肤?为什么 inverseBindMatrices 绑定到皮肤而不是关节?
- 当我从关键帧(每个骨骼)进行正确的旋转、缩放、平移时,如何计算我需要传递给顶点着色器的每个骨骼的矩阵?
- 文件中的每个骨骼节点都有自己的旋转、平移、缩放值但没有矩阵。这是为什么?是不是少了什么?
- gltf 文件引用骨骼(关节)作为节点 ID,但作为属性传递给着色器的 weights/jointId-Arrays 与这些骨骼 ID 不匹配:jointIds-Array 包含即 0,1 ,2 表示骨骼 ID,但骨骼位于节点 4、5、6 - 如何为传递给着色器的每个 jointId 找到正确的骨骼?
希望你能帮助我。亲切的问候!! 如果需要,我可以提供更多我的代码,但我认为如果我从整体上理解了这个主题,那么我就可以自己完成了。
编辑
编辑 #2
好的,我想我正在掌握它的窍门......慢慢地。
对于每个由骨架控制的Mesh,文件中都有一个Skin。我认为每个网格都需要一个反向绑定矩阵,以便能够将网格转换为骨骼 space(并且 - 如果需要 - 返回)。
我仍然不知道如何在将它们传递给着色器之前正确计算最终变换。
这一点我还是不懂。
由于每个皮肤都有一个包含三个(或最多 4 个)关节的列表,因此这些是最终转换需要传递给顶点着色器的关节。如果你有 8 个关节,但当前要绘制的网格仅受其中 4 个的影响,为什么你应该传递所有 8 个矩阵而不是只传递你需要的 4 个。
这一切仍然笼罩着疑问。也许这对其他人有帮助。
我正在尝试一一解答您的问题
- 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 的骨骼定义的任何关节=] 骨骼骨骼层次结构中的骨骼。