三角形网格管理构成场景中对象的三角形集合,但图形应用程序中的另一个普遍问题是将对象安排在所需的位置。正如我们在第7章中看到的,这是使用转换完成的,但是复杂的场景可以包含大量的转换,组织好它们会使场景更容易操作。大多数场景都采用层次结构,并且可以使用场景图根据这个层次结构来管理转换。
为了引出场景数据结构,我们将使用如图12.19所示的铰链式的钟摆结构。考虑一下我们如何绘制钟摆的顶部:
底部更复杂,但我们可以利用它附着在局部坐标系中b点的上摆底部。首先,我们旋转较低的摆,使其与初始位置成$\phi$角。然后,我们移动它,使它的顶部铰链在点b。现在它在上摆局部坐标的适当位置,然后它可以沿着该坐标系移动。下摆的复合变换为:
因此,我们不仅看到下摆存在于它自己的局部坐标系中,而且其坐标系本身也随着上摆的坐标系移动。
我们可以将钟摆编码到数据结构中,这样可以更容易地管理这些坐标系统问题,如图12.20所示。应用于对象的适当矩阵就是从对象到数据结构根的链中所有矩阵的乘积。例如,考虑一个轮渡模型,其中有一辆汽车可以在轮渡的甲板上自由移动,每个轮子都相对于汽车移动,如图12.21所示。
就像摆一样,每个物体都应该由从根到该物体的路径上的矩阵的乘积进行变换:
在栅格化的情况下,可以使用矩阵堆栈(许多api支持的数据结构)实现有效的实现。矩阵堆栈是使用push和pop操作来操作的,这些操作从矩阵乘积的右侧添加和删除矩阵。例如,调用
push(M0)
push(M1)
push(M2)
创建活动矩阵M = M0M1M2。随后调用pop()删除最后添加的矩阵,以便活动矩阵变成M = M0M1。将矩阵堆栈与场景图的递归遍历相结合,可以得到
function traverse(ndoe) {
push(M_local)
draw object using composite matrix from stack
traverse(left child)
traverse(right child)
pop()
}
场景图中的代码实现有很多变种实现,但都遵循上面的基本思想。