五、通用树的封装
2、理论
对于游戏开发或计算机图形编程来说,树数据结构可能是应用最广泛的数据结构之一。可以使用树结构来表示场景图渲染系统、角色动画中的骨骼系统、具有层次性的空间分割系统或者是层次包围体系统等。
树是一类非线性数据结构。树的相关概念:
- 节点: 每个元素称为节点
- 树根:根节点
- 度:一个节点含有的子节点的个数称为该节点的度
- 叶子节点:度为 0 的节点
1、二叉树
2、四叉树
四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构。
它将已知范围的空间等分成四个相等的子空间,如此递归下去,直至树的层次达到一定深度或者满足某种要求后停止分割。
四叉树的结构在空间数据对象分布比较均匀时,具有比较高的空间数据插入和查询效率
3、八叉树
八叉树的结构和四叉树基本类似,其拥有8个节点(三维2元素数组),其构建方法与查询方法也大同小异。
4、松散四叉树/八叉树
四叉树/八叉树的一个问题是,物体有可能在边界处来回,从而导致物体总是在切换节点,从而不得不更新四叉树/八叉树。
而松散四叉树/八叉树正是解决这种边界问题的一种方式:
首先它定义一个节点有入口边界(inner boundary),出口边界(outerboundary)。
那么如何判定一个物体现在在哪个节点呢?
- 若物体还没添加进四叉树/八叉树,则检测现在位于哪个节点的入口边界内;
- 若物体先前已经存在于某个节点,则先检测现在是否越出该节点的出口边界,若越出再检测位于哪个节点的入口边界内。
- 一篇关于松散四叉树/八叉树的论文里,实验表明出口边界长度为入口边界2倍时可以表现得很好
这里不详细赘述,感兴趣可以深入了解。
5、层次包围盒树(Bounding Volume Hierarchy Based On Tree)
层次包围盒树(BVH树)是一棵多叉树,用来存储包围盒形状。
它的根节点代表一个最大的包围盒,其多个子节点则代表多个子包围盒。
常用的层次包围盒树有AABB层次包围盒树和球体树。
AABB包围盒树
下图为层次AABB包围盒树。把不同形状粗略用AABB形状围起来看作一个AABB形状(为了统一化形状),然后才建立层次AABB包围盒树。
在物理引擎里,由于物理模拟,大部分形状都是会动态更新的,例如位移/旋转都会改变形状。于是就又有一种支持动态更新的层次包围盒树,称之为动态层次包围盒树。
球体包围盒树
球体是最容易计算的一类包围盒,而且球体树构造速度可以很快,因此球体树可被用作粗略松散但快速的空间划分结构。
快速构造松散球体树的步骤(以三角形物体为例):
- 计算出包围所有三角边顶点的最小球体包围盒,作为根节点
- 以球心为坐标系原点,其坐标系X轴划分出在该X轴左右的三角形,并将这些分别放入左子节点、右子节点中
- 重复步骤1、2,最后得到一棵球体树
在步骤2中,还可以按X轴,Y轴,Z轴的顺序轮流划分,即第一次步骤2划分用X轴,第二次步骤2划分用Y轴…
BSP树 (Binary Space Partitioning Tree)
BSP tree是一棵二叉树,中文译名为二维空间分割树。
BSP tree在3D空间下其每个节点表示一个平面,其代表的平面将当前空间划分为前向和背向两个子空间,分别对应左儿子和右儿子。
2D空间下,BSP树每个节点则表示一条边,也可以将2D空间划分成前后两部分。
k-d树 (k-dimensional tree)
k-d树是一棵二叉树,其每个节点都代表一个 k维坐标点:
- 树的每层都是对应一个划分维度(取决于你定义第i层是哪个维度)
- 树的每个节点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树
举例,一棵k-d树(k=2)的结构如图:
根据第一层划分维度为X,第二层为Y,第三层为X,
所以该k-d树(k=2)对应代表划分的空间,看起来应该是这样的:
6、总结
数据结构 | 适用情形 | n的数量级 | 构造所需时间 | 是否可以动态更新 | 占用空间 |
---|---|---|---|---|---|
四叉树 | 场景管理(基于地形或不含高度)、渲染 | 物体数量 | O(n*logn) | 是 | 大(取决于区域大小和物体数量) |
八叉树 | 场景管理、渲染 | 物体数量 | O(n*logn) | 是 | 大(取决于区域大小和物体数量) |
BVH树 | 任何情形(包括物理、渲染) | 物体数量 | O(n*logn) | 是 | 中(取决于物体数量) |
BSP树 | 编辑器、复杂室内场景 | 平面数量 | O(n*log²n) | 否 | 大(取决于平面数量) |
k-d树 | - | 物体数量 | O(knlogn) | 否 | 中(取决于物体数量) |
7、通用树的封装及遍历
这里代码封装的树支持二叉树、四叉树、八叉树等,是一种非严格的通用树结构。
同时支持 8 种遍历方式
树的遍历这里分为 3 类 共计 8 种:
广度优先(层次)、深度优先
从上到下(先根/前序) 或 从下到上 (后根/后序)
从左到右 或 从右到左
拿上面的树数据结构为例,采用八种遍历方式得到的遍历结果分别为:
本章参考如下:
《TypeScript 图形渲染实战——基于WebGL的3D架构与实现》