【译】glTF教程:一个简单的动画(六)
一个简单的动画
如场景和节点部分所示,每个节点都可以有一个本地转换。这个转换既可以通过节点的“矩阵”属性给出,也可以通过使用“平移”、“旋转”和“缩放”(TRS)属性给出。
当TRS属性给出转换时,可以使用animation
来描述节点的’平移’、’旋转’或’缩放’如何随时间变化。下面是前面显示的示例:最小的glTF文件,但通过动画进行了扩展。本节将解释添加此动画所做的更改和扩展。
{
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0,
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
}
],
"meshes" : [
{
"primitives" : [ {
"attributes" : {
"POSITION" : 1
},
"indices" : 0
} ]
}
],
"animations": [
{
"samplers" : [
{
"input" : 2,
"interpolation" : "LINEAR",
"output" : 3
}
],
"channels" : [ {
"sampler" : 0,
"target" : {
"node" : 0,
"path" : "rotation"
}
} ]
}
],
"buffers" : [
{
"uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=",
"byteLength" : 44
},
{
"uri" : "data:application/octet-stream;base64,AAAAAAAAgD4AAAA/AABAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAPT9ND/0/TS/AAAAAAAAAAAAAAAAAACAPw==",
"byteLength" : 100
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : 6,
"target" : 34963
},
{
"buffer" : 0,
"byteOffset" : 8,
"byteLength" : 36,
"target" : 34962
},
{
"buffer" : 1,
"byteOffset" : 0,
"byteLength" : 100
}
],
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 3,
"type" : "SCALAR",
"max" : [ 2 ],
"min" : [ 0 ]
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 3,
"type" : "VEC3",
"max" : [ 1.0, 1.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0 ]
},
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 5,
"type" : "SCALAR",
"max" : [ 1.0 ],
"min" : [ 0.0 ]
},
{
"bufferView" : 2,
"byteOffset" : 20,
"componentType" : 5126,
"count" : 5,
"type" : "VEC4",
"max" : [ 0.0, 0.0, 1.0, 1.0 ],
"min" : [ 0.0, 0.0, 0.0, -0.707 ]
}
],
"asset" : {
"version" : "2.0"
}
}
节点的“旋转”属性
(https://en.wikipedia.org/wiki/Quaternion) that describes the rotation:
示例中唯一的节点现在具有rotation
属性。这是一个包含quaternion四个浮点值的数组,描述了旋转:
"nodes" : [
{
"mesh" : 0,
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
}
],
给定的值是描述“0度旋转”的四元数,因此三角形将显示在其初始方向。
动画数据
在glTF JSON的顶层数组中添加了三个元素对动画数据进行编码:
- 包含原始动画数据的新“缓冲区”;
- 引用缓冲区的新“bufferView”;
- 两个新的“访问器”对象,添加结构信息到动画数据。
原始动画数据的“buffer”和“bufferView”
添加了一个新的“缓冲区”,其中包含原始动画数据。这个缓冲区还使用一个 基本glTF结构 来编码动画数据包含的100个字节:
"buffers" : [
...
{
"uri" : "data:application/octet-stream;base64,AAAAAAAAgD4AAAA/AABAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAPT9ND/0/TS/AAAAAAAAAAAAAAAAAACAPw==",
"byteLength" : 100
}
],
"bufferViews" : [
...
{
"buffer" : 1,
"byteOffset" : 0,
"byteLength" : 100
}
],
还有一个新的“bufferView”,它在这里只是指索引1的新“buffer”,其中包含整个动画缓冲区数据。下面描述的“访问器”对象添加了进一步的结构信息。
注意,还可以将动画数据附加到已经包含三角形几何数据的现有缓冲区中。在本例中,新的buffer视图将引用索引为0的“buffer”,并使用适当的“byteOffset”来引用随后包含动画数据的缓冲区部分。
在这里显示的示例中,动画数据被添加为一个新的缓冲区,以保持几何数据和动画数据分离。
动画数据的“访问器”对象
添加了两个新的“访问器”对象,它们描述了如何解释动画数据。第一个访问器描述动画关键帧的times。有5个元素(由count(5)表示),每个元素都是标量“float”值(总共20个字节)。第二个访问器表示,在前20个字节之后,有5个元素,每个元素都是带有“浮动”组件的4D向量。这些是对应动画的五个关键帧的旋转,以四元数的形式给出。
"accessors" : [
...
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 5,
"type" : "SCALAR",
"max" : [ 1.0 ],
"min" : [ 0.0 ]
},
{
"bufferView" : 2,
"byteOffset" : 20,
"componentType" : 5126,
"count" : 5,
"type" : "VEC4",
"max" : [ 0.0, 0.0, 1.0, 1.0 ],
"min" : [ 0.0, 0.0, 0.0, -0.707 ]
}
],
times accessor和 rotate accessor使用示例中缓冲区的数据提供的实际数据如下表所示:
times accessor | rotations accessor | Meaning |
---|---|---|
0.0 | (0.0, 0.0, 0.0, 1.0 ) | At 0.0 seconds, the triangle has a rotation of 0 degrees |
0.25 | (0.0, 0.0, 0.707, 0.707) | At 0.25 seconds, it has a rotation of 90 degrees around the z-axis |
0.5 | (0.0, 0.0, 1.0, 0.0) | At 0.5 seconds, it has a rotation of 180 degrees around the z-axis |
0.75 | (0.0, 0.0, 0.707, -0.707) | At 0.75 seconds, it has a rotation of 270 (= -90) degrees around the z-axis |
1.0 | (0.0, 0.0, 0.0, 1.0) | At 1.0 seconds, it has a rotation of 360 (= 0) degrees around the z-axis |
这个动画描述了绕z轴旋转360度,持续1秒。
动画
最后,这是添加实际动画的部分。顶层的“动画”数组包含一个“动画”对象。它包括两个要素:
samplers
,描述动画数据的来源;channels
,可以想象为将动画数据的“源”连接到“目标”。
在给定的示例中,有一个采样器。每个采样器定义一个“输入”和一个“输出”属性。它们都引用访问器对象。在这里,这些是上面描述的times访问器(带有索引2)和rotate访问器(带有索引3)。此外,采样器定义了interpolation
类型,在本例中是"LINEAR"
类型。
示例中还有一个“通道”。该通道引用唯一的采样器(索引0)作为动画数据的源。动画的目标被编码在channel.target
对象:它包含一个’ id ‘,指的是属性应该被动画化的节点。实际的节点属性在“path”中命名。因此,在给定的示例中,通道目标表示索引为0的节点的 "rotation"
属性应该被动画化。
"animations": [
{
"samplers" : [
{
"input" : 2,
"interpolation" : "LINEAR",
"output" : 3
}
],
"channels" : [ {
"sampler" : 0,
"target" : {
"node" : 0,
"path" : "rotation"
}
} ]
}
],
结合所有这些信息,给定的动画对象如下所示:
在动画过程中,动画值是通过* rotate * accessor获得的。根据当前模拟时间和times访问器提供的关键帧时间,对它们进行线性插值。然后将插值写入索引为0的节点的
"rotation"
属性中。
在动画小节中可以找到关于插值和计算的更详细的描述和实际示例。