Table of Contents
Scenes
在一个glTF文件中可能会有多个场景(scenes),但是在大多数情况下,只会有一个场景,也是默认的场景。每个场景包括一个nodes的数组,里面存储场景图的根结点(root nodes)的索引。类似的,根结点可能会有多个,形成不同的层次关系,然而在大多数情况下,一个场景只会有一个根结点。
Nodes forming the scene graph
每个node都可以有一个数组children的子节点(child nodes)。每个结点都是结点层次结构中的一个元组,它们共同将场景的结构定义成场景图(scene graph)。
Image1: The scene graph representation stored in the glTF JSON
可以遍历场景中给定的每个节点,以递归方式访问其所有子节点,以处理连接到节点的所有元素。 此遍历的简化伪代码可能如下所示:
traverse(node) {
// Process the meshes, cameras, etc., that are
// attached to this node - discussed later
processElements(node);
// Recursively process all children
for each (child in node.children) {
traverse(child);
}
}
实际上,遍历将需要一些附加信息:附加到节点的某些元素的处理将需要有关它们附加到哪个节点的信息。 另外,必须在遍历期间累积(就是多个变换想乘)有关节点变换的信息。
Local and global transforms
每个结点都可以有一个转换/变换(transform)。这个转换(transform)会定义平移(translation)、旋转(rotation)和/或缩放(scale)。这个转换(transform)会被应用到附加结点本身和它的所有子节点。结点的层次关系孕育人们构造应用于场景元素的平移、旋转和缩放。
Local transforms of nodes
结点的局部变换可以有多种不同的可能表示形式,第一种是用矩阵表示,第二种是用TRS三个属性表示。
变换信息可以直接由结点的矩阵属性(matrix property)给出。这是一个由16个浮点数组成的矩阵。例如,以下矩阵描述了大约(2,1,0.5)的缩放比例,围绕x轴大学30°的旋转以及(10,20,30)的平移。
"node0": {
"matrix": [
2.0, 0.0, 0.0, 0.0,
0.0, 0.866, 0.5, 0.0,
0.0, -0.25, 0.433, 0.0,
10.0, 20.0, 30.0, 1.0
]
}
这里定义的矩阵也可见于图2。
Image 2: An example matrix.
一个结点的变换也可以通过结点的translation、rotation、scale属性来给出,有时候会缩写成TRS:
"node0": {
"translation": [ 10.0, 20.0, 30.0 ],
"rotation": [ 0.259, 0.0, 0.0, 0.966 ],
"scale": [ 2.0, 1.0, 0.5 ]
}
这些属性中的每一个都可以用于创建矩阵,这些矩阵的乘积就是结点的局部变换。
1. translation 平移
平移只包含x、y、z方向上的平移。例如,从平移属性[10.0, 20.0, 30.0],可以生成一个变换矩阵(见图3),平移的变换属性在矩阵的最后一列中。
Image 3: A translation matrix
2. rotation 旋转
旋转属性通过四元数(quaternion)给定。quaternion是旋转变换的一种紧凑表示,可以表示围绕任意轴旋转任意角度的旋转情况。例如,四元数[0.259, 0.0, 0.0, 966]描述围绕x轴旋转大约30°,该四元数可以转换为旋转矩阵(见图4)。
Image 4: A rotation matrix.
3. scale 缩放
缩放包括沿x轴、y轴、z轴的比例因子。将这些比例因子放置在矩阵的对角线上,就能创建出相应的矩阵。以比例因子[2.0, 1.0, 0.5]可以创建出缩放矩阵(见图5)
Image 5: A scale matrix
4. M=T*R*S
在计算结点的最终局部变换矩阵时,相乘T、R、S的矩阵就可以了。以正确的顺序执行这些矩阵的乘法很重要。局部的变换矩阵应该以算式M=T*R*S计算,T是平移变换矩阵,R是旋转平移矩阵,S是缩放变换矩阵。计算的伪代码为:
translationMatrix = createTranslationMatrix(node.translation);
rotationMatrix = createRotationMatrix(node.rotation);
scaleMatrix = createScaleMatrix(node.scale);
localTransform = translationMatrix * rotationMatrix * scaleMatrix;
根据上面的例子,可以计算最终的局部变换矩阵:
Image 6: The final local transform matrix computed from the TRS properties.
这个矩阵会使得mesh中的顶点被缩放、旋转、平移,这几个变换都是基于结点的缩放、旋转、平移属性进行变换。
如果未指定这三个属性中的任何一个,则会使用单位矩阵。同样,当一个结点既不包含矩阵属性也不包含TRS属性时,其变换矩阵将为默认的单位矩阵(不进行任何变换)。
Global transforms of nodes
无论JSON文件中如何表示,结点的局部变换都可以存储为4×4的矩阵。结点的全局变换由根到相应的结点的路径上所有局部变换的乘积给出:
Structure: local transform global transform
root R R
+- nodeA A R*A
+- nodeB B R*A*B
+- nodeC C R*A*C
值得指出的是,成功加载文件后,这些全局变换不能仅计算一次。在后面的教程中,将会详细说明animations(动画)会如何修改单个结点的局部变换。这些修改将影响所有子节点的全局变换。因此,当需要得到一个结点的全局变换时,必须直接从所有结点的当前局部变换计算得到它。为了提高性能,可以缓存全局变换,检测祖先结点的局部变换中的变化,并仅在必要时更新全局变换。不同的实现选择取决于编程语言和客户端应用程序的要求,不在教程的讨论范围内。