【空间加速结构】——BSP tree和k-d tree
BSP Tree(Binary space partitioning Tree)
BSP(二叉空间分割)树,在游戏工业界非常著名,最早应用的游戏是《Doom》。由于当时的硬件没有Z-Buffer,用BSP树可以实现从后往前渲染保证正确性。当然它也可以用于加速碰撞检测和相交测试。
在计算机图形学中,BSP树有两种形式:多边形对齐(pologyon-aligned)和轴对齐(axis-aligned)。构建BSP树的步骤是选择一个超平面将空间分为两个子空间,并将空间中的geometry划分到这两个空间中,然后递归划分子空间,直到树的深度达到限制条件或geometry被划分完毕时终止。
多边形对齐BSP树
《Doom》用的就是这种BSP树。在多边形对齐BSP树中,多边形的面被选做分割空间的平面。具体的分割方法是:
-
选一个片元,加入到根节点中。
-
用这个片元所在的平面将场景中的多边形分为两组。
- 如果多边形与这个片元所在的平面相交,那么把这个多边形沿分割平面分为两部分,分别划分到被平面分割的两个空间中。
-
对分割的子空间递归执行1,2,直到所有多边形的片元都在BSP树中。
考虑到性能,我们要建立一颗左右子树平衡的BSP树,所以在选择分割平面时要保证左右两个空间的多边形数量基本一致。
由于需要N次划分,每次划分后,要在子集中遍历划分平面(logN次遍历),并计算平面左右的片元的数量(logN),以挑选能使左右子树片元数量基本相等的划分平面,所以构造多边形对齐BSP树的时间复杂度为O(NlogN*logN)。
轴对齐BSP树
构造轴对齐BSP树的方法如下。
- 对要分割的空间,选取其AABB的某个轴,以与这个轴垂直的平面将该空间分为两个部分,并将空间中的物体划分到对应的空间。
- 对子空间递归执行步骤1,直到树深度到达限制或AABB中包含的图元低于某阈值。
同四叉树一样,会碰到物体与分割平面相交的情况,这个问题的解决思路同四叉树一致,不再赘述。
当碰到这样的划分情况,根节点选择垂直x轴的平面划分,二层的节点选择垂直y轴的平面划分,三层的平面沿着垂直z轴的平面划分,以此类推并循环。这种BSP树叫做k-d tree,在下面介绍。
K-d tree(k-dimensional tree)
k-d tree是一种特殊的BSP树,它的特点有:
- 每一层都是一种划分维度
- 每个节点代表垂直于当前维度的超平面,将空间划分为两部分
- k维空间,按树的每一层循环选取,当前节点为i维,下一层节点为(i+1)%k维。
以下面这个二维空间举例,建立k-d tree以理解它的建树方法。
这样一个空间,以(7, 2)处为根节点,分别以X轴,Y轴,X轴为划分维度,划分结果是:
K-d tree虽然可以划分区域但是不好用,当节点发生变化时建树比较麻烦。
但它很适合用来做最临近查找。
用上述构建好的k-d tree为例,搜索(3, 5)的最近邻,
- 首先从根节点出发,将根节点设为最近邻,对tree进行深度优先遍历。以(3, 5)为圆心,到(7,2)的距离为半径做圆,可以看出(8,1)右侧的空间与该圆不相交,所以这个子树被剪枝。
- 从根节点递归遍历到左子数根节点(5,4),最近邻得到更新,同理以到最近邻的距离做圆,可以剪枝掉(7, 2)的右侧空间。
- 继续遍历(5, 4)的左右节点,发现与最近距离相等,不必更新最近邻。
- 最后可得最近邻为(5, 4)。
kd-tree一般与四叉树/八叉树混合结合使用,它们用来做大粒度的划分和查找,使用k-d tree进行临近的划分和查找。
参考
[1] 空间数据结构(四叉树/八叉树/BVH树/BSP树/k-d树) - KillerAery - 博客园
[3] Real-Time Rendering, Third Edition