Aud*_*Guy 5 c# opengl animation skinning gltf
我有一个简单的混合器模型,它由三个网格组成,三个网格各控制一个网格。动画只是骨骼围绕 y 轴稍微旋转立方体并返回。中心骨骼是两个外部骨骼的父骨骼。
然后,我使用 GLTF2.0(文本版本)导出插件导出此场景,现在尝试将其导入到我新制作的 opengl 引擎(c# xamarin android)中。
由于我想完全理解GLTF2.0格式和OpenGL中的骨骼动画,所以我尝试自己实现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
}
]
Run Code Online (Sandbox Code Playgroud)
这让我很困惑,因为所有网格体都有一个骨骼结构,而不是每个网格体有三个骨骼。我想我会收集类实例(例如 Bone.cs)中的所有骨骼,每个骨骼都有一个子骨骼列表及其父骨骼字段。然后我会在实例(Animation.cs 类)中收集动画,每个动画实例都会有一个关键帧列表,其中包含给定时间戳的旋转、缩放和平移。当动画时间戳设置为 2.5 秒时,我查找该时间戳最近的两个关键帧,并对这些关键帧插入旋转、缩放和平移。
我希望你可以帮助我。亲切的问候!!如果需要,我可以提供更多代码,但我认为如果我从整体上理解了该主题,我就可以自己完成。
编辑
编辑#2
好吧,我想我正在慢慢掌握它的窍门。
对于由骨架控制的每个网格,文件中都有一个皮肤。我认为每个网格体都需要有一个逆绑定矩阵,以便能够将网格体转换为骨骼空间(以及 - 如果需要的话 - 返回)。
我仍然不知道如何在将最终变换传递给着色器之前正确计算它们。
这一点我仍然不明白。
由于每个外观都有一个包含三个(或最多 4 个)关节的列表,因此这些关节的最终变换需要传递给顶点着色器。如果您有 8 个关节,但当前要绘制的网格仅受其中 4 个关节的影响,为什么要传递所有 8 个矩阵,而不是只传递您需要的 4 个矩阵。
这一切仍然笼罩着疑问。也许这对其他人有帮助。
我正在尝试一一解答你的问题
- 为什么会有三款皮肤?为什么 inverseBindMatrices 绑定到皮肤而不是关节?
正如您已经发现的,每个网格有一个蒙皮。事实上,在您的具体情况下,您可以将所有三个网格合并为一个,这一事实并没有真正限制这一一般原则。然而
我认为每个网格体都需要有一个逆绑定矩阵,以便能够将网格体转换为骨骼空间(以及 - 如果需要的话 - 返回)。
每个网格的每个关节都有一个逆绑定矩阵。该属性的名称是复数形式是有原因的,它引用了 a ,而 a 又引用了 a 中的一些数据。inverseBindMatricesbufferviewbuffer
在这里改变你的问题的顺序,因为这样会更有意义:
文件中的每个骨骼节点都有自己的旋转、平移、缩放值,但没有矩阵。这是为什么?是不是少了点什么?
你还需要什么?每个仿射变换都可以分解为平移、旋转和缩放,因此数据是完整的。glTF 规范定义结果矩阵应按 order 计算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 找到正确的骨骼?
该jointIds数组包含对关节的引用,而不是骨骼(因此得名)。蒙皮着色器根本不关心骨骼,骨骼所做的只是定义此处关节的层次结构,因此它们会影响 的实际值,M_global从而影响M_joint矩阵的实际值。第i-th 条目仅引用相应皮肤数组i中的第 -th 关节,因此它需要.jointsM_joint(i)
- 由于每个外观都有一个包含三个(或最多 4 个)关节的列表,因此这些关节的最终变换需要传递给顶点着色器。
为什么要skin限制为 4 个关节。皮肤可以有任意数量的关节。
如果您有 8 个关节,但当前要绘制的网格仅受其中 4 个关节的影响,为什么要传递所有 8 个矩阵,而不是只传递您需要的 4 个矩阵。
为什么要为只需要 4 个骨骼的网格定义 8 个骨骼的蒙皮?glTF 数据格式不会阻止您存储不相关的信息,或以低效的方式存储信息。
这里需要注意的一点是,关节之间的层次结构仍然由 的骨骼节点层次结构定义skeleton。因此,您可以在单个 中省略任意关节skin,但这些骨骼节点(以及它们的潜在动画)仍然可以影响最终的关节矩阵 - 对于由骨骼定义的任何关节,该骨骼位于骨架中“省略”骨骼的下方骨层次结构。