原文:https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md
一个glTF文件中可能有多个场景scene
被存储,但在大多数情况下,只有一个场景,也就是默认场景。每个场景都包含一组节点nodes
,这些节点是场景图(scene graph)根节点的索引 indices。同样的,可能有多个根节点,形成不同的层次,但在许多情况下,场景只有一个根节点。最简单的场景描述包含单个节点、单个场景,已在上一节中解释:
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0
}
],
每个节点node
都可以包含一个名为children
的数组,该数组包含其子节点的索引。因此每个节点都是节点层次结构中的一个元素,它们一起定义场景的结构(场景图 scene graph)。
可以遍历场景中scene
给定的每个节点,递归地访问其所有子节点,从而能够处理附加到节点上的所有元素。遍历过程中的简化代码如下所示:
traverse(node) {
// 处理 网格,相机等元素, 这些元素是附着在node节点上的
//后面再讨论如何处理这些元素: processElements(node);
// 递归处理所有的子对象。
for each (child in node.children) {
traverse(child);
}
}
实际上,遍历需要一些额外的信息:处理附加到节点的某些元素,需要知道它们附加到哪个节点。此外,在遍历过程中,关于节点的矩阵变换(transform)信息需要被逐步积累(accumulated)
每个节点都可以有一个矩阵(或者成为 变换矩阵)。这样的矩阵定义了平移(translation)、旋转(rotation)和缩放(scale)。此矩阵将应用于附加到节点本身及其所有子节点的所有元素之上。因此,节点的层次结构(hierarchy of nodes)允许构造应用于场景元素的平移、旋转和缩放。
节点的局部矩阵变换有不同的表示方法。该变换可由节点的矩阵matrix
直接给出。这是一个由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
]
}
节点的矩阵转换也可以使用节点的平移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 ]
}
这些属性中的每一个都可以用来创建一个矩阵,然后这些矩阵的乘积(product:矩阵乘积)就是节点的局部变换:
translation平移
只包含了 x-, y-和 z方向的平移. 例如,从vector3 [10.0,20.0,30.0]
的平移中,可以创建一个平移矩阵,体现在矩阵中的最后一列,如图4c所示。rotation
是以四元数的形式给出的。四元数的数学背景超出了本教程的范围。目前,最重要的不部分是四元数是围绕任意角度和任意轴旋转的表示。例如,四元数vector4[0.259,0.0,0.0,0.966]
描述绕x轴旋转约30度。这个四元数可以被转换成旋转矩阵,如图4d所示。-> 此处给出了 四元数到旋转矩阵的转换方式: https://blog.csdn.net/loongkingwhat/article/details/88427828scale
包含了沿着x,y,z轴的放缩系数 ;通过使用这些比例因子作为矩阵对角线上的数字,可以创建相应的矩阵。例如,缩放因子vector3[2.0、1.0、0.5]
的缩放矩阵如图4e所示。计算节点的最终局部变换矩阵时,需要将各自的矩阵相乘在一起,并且必须按正确的顺序对这些矩阵进行乘法运算。局部变换矩阵公式为M=T*R*S
,其中T
是平移部分的矩阵,R
是旋转部分的矩阵,S
是缩放部分的矩阵。计算代码示意为:
translationMatrix = createTranslationMatrix(node.translation);
rotationMatrix = createRotationMatrix(node.rotation);
scaleMatrix = createScaleMatrix(node.scale);
localTransform = translationMatrix * rotationMatrix * scaleMatrix;
对于上面给出的示例矩阵,节点的最终局部变换矩阵如图4f所示。
图4f: 根据TRS属性计算的最终局部变换矩阵。
此矩阵将使得网格的顶点先 缩放、再旋转,最后再平移(注意与乘法法则正好倒过来)
当这三个性质中的任何一个没有给出时,将使用单位矩阵(identity matrix)。类似地,当一个节点既不包含矩阵matrix
属性也不包含TRS属性时,它的局部变换也将是单位矩阵(identity matrix)。
无论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
必须指出的是,加载文件后,这些全局转换不能只计算一次。稍后,将演示动画中(animation)如何修改单个节点的局部变换。这些修改将影响所有子节点的全局转换。因此,当需要节点的全局变换时,必须直接从所有节点的当前局部变换计算。或者,作为潜在的性能改进,实现可以缓存全局转换,检测祖节点(ancestor node)的局部转换中的更改,并仅在必要时更新全局转换。这方面的不同实现选项将取决于编程语言和客户机应用程序的要求,因此超出了本教程的范围。