【译】glTF教程:一个简单的动画(六)

【译】glTF教程:一个简单的动画(六)

2019-02-12

一个简单的动画

场景和节点部分所示,每个节点都可以有一个本地转换。这个转换既可以通过节点的“矩阵”属性给出,也可以通过使用“平移”、“旋转”和“缩放”(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"
  }
  
}


图6a:一个动画三角形。

节点的“旋转”属性

(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"属性中。

动画小节中可以找到关于插值和计算的更详细的描述和实际示例。