CSDN视频 址:http://edu.csdn.net/lecturer/144
骨骼动画实际上是两部分的过程。第一个由美术执行,第二个由程序员(或者你写的引擎)执行。第一部分发生在建模软件中,称为建模。这里发生的是术定义了 格下面骨骼的骨架。 格代表物体(无论是人类,怪物还是其他物体)的皮肤,骨骼用于移动 格物体,以模拟现实世界中的实际运动,这通过将每个顶点分配给一个或多个骨头来完成。当顶点被分配给骨骼时,定义了权重,该权重确定骨骼在移动时对顶点的影响量。通常的做法是使所有权重的总和1(每个顶点)。例如,如果一个顶点位于两个骨骼之间,我们可能希望将每个骨骼的权重分配为0.5,因为我们希望骨骼在顶点上的影响相等。然而,如果顶点完全在单个骨骼的影响之内,那么权重将为1(这意味着骨骼自主地控制顶点的运动)。
这是一个在混合器中创建的骨骼结构的例子:
骨骼ID是骨转换数组的索引, 这些变换将被应用在WVP矩阵之前的位置和正常(即它们将顶点从“骨空间”转换成局部空间)。 权重将用于将几个骨骼的变换组合成单个变换,并且在任何情况下,总权重必须正好为1(建模软件的事情)。 通常,我们将在动画关键帧之间进行插值,并在每个帧中更新骨骼变换数组。
骨骼转换阵列的创建方式通常是棘手的部分。 变换被设置在一个历史结构(即树)中,通常的做法是在树中的每个节点中具有缩放向量,旋转四元数和平移向量。 实际上,每个节点都包含这些项目的数组。 数组中的每个条目都必须有一个时间戳。 应用时间与其中一个时间戳完全匹配的情况可能很少,因此我们的代码必须能够插值缩放/旋转/转换,以便在应用程序的时间点获得正确的转换。 我们对每个节点从当前骨到根进行相同的过程,并将这个变换链相加在一起以获得最终结果。 我们为每个骨骼做这些,然后更新着色器。
到目前为止,我们谈到的一切都是非常通用的。 但是这是一个关于使用Assimp的骨骼动画的博文,所以我们需要再次进入该库,读者可以自行下载一个Assimp库,看看如何使用它进行皮肤化。 Assimp的好处是它支持从多种格式加载骨骼信息。 不好的是,您仍然需要对其创建的数据结构进行相当多的工作,以生成您为着色器所需的骨骼转换。
让我们从根的骨骼信息开始吧, 以下是Assimp数据结构中的相关内容:
再次,我们从aiScene开始, aiScene对象包含一个指向aiNode类对象的指针,该对象是一个节点层级的根(换句话说 -一棵树), 树中的每个节点都有一个指向其父项的指针以及指向其子节点的数组, 这样我们可以方便地来回遍历树。 另外,节点执行从节点空间变换到其父节点空间的变换矩阵。 最后,节点可能有也可能没有一个名字。 如果一个节点表示父进制中的骨骼,则节点名称必须与骨骼名称相匹配。 但是有时节点没有名称(这意味着没有相应的骨骼),而且他们的工作只是帮助模型分解模型并且沿着一些中间变换。
最后一块拼图是aiAnimation数组,它也存储在aiScene对象中, 单个aiAnimation对象表示一系列动画帧,例如“walk”,“run”,“shoot”等。通过在帧之间进行内插,我们得到与动画名称相匹配的所需视觉效果。 动画的持续时间为每秒钟的秒数(例如每秒100个刻度和25个刻度,代表4秒动画),这有助于我们对进程进行时间调整,以使动画在每个硬件上看起来相同。 另外,动画还有一个名为通道的aiNodeAnim对象的数组。 每个通道实际上都是骨骼,全部是它的转变。 该通道包含一个名称,该名称必须与其他一个节点在层级和三个转换数组中匹配。
为了计算特定时间点的最终骨骼变换,我们需要在这三个阵列中的每一个中找到与时间匹配的两个入口,并在它们之间插值。 那么我们需要将转换组合成一个矩阵。 做完之后,我们需要在根中找到相应的节点。 然后我们需要相应的通道为父,并进行相同的插值过程。 我们把这两个变化相乘合起来,直到我们达到根的层级。
加载模型的源代码实现如下:
这是更新到Mesh类的入口点,更改标记为粗体,有一些我们需要注意的变化。 一个是导入和aiScene对象现在是类成员,而不是堆栈变量。(关于阿Assimp模型的加载会在后面博客中讲解) 原因是在运行时,我们将一次又一次地返回到aiScene对象,因此我们需要扩展导入器和场景的范围。 在一个真实的游戏中,您可能想要复制所需的东西,并以更优化的格式存储。
第二个变化是提取,反转和存储了根的层级转换矩阵, 我们继续看下去。 请注意,矩阵逆的代码已从Assimp库复制到我们的Matrix4f类中。
源代码的实现如下所示:
上面的结构包含了我们在顶点级别所需要的一切, 默认情况下,我们有足够的存储空间用于四个骨骼(每个骨骼的ID和权重)。 VertexBoneData的结构就像这样,使之简单的传递给着色器。 我们已经分别在位置0,1和2处获得了位置,纹理坐标和法线。 因此,我们配置的VAO来绑定位置3处的骨骼ID和位置4处的权重。请注意,我们使用glVertexAttribIPointer而不是glVertexAttribPointer来绑定ID非常重要。 原因是ID是整数而不是浮点。 注意这一点,否则您将在着色器中收到损坏的数据。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!