【空间加速结构】——四叉树
四叉树是一种对空间进行划分的数据结构。它将空间分为四个相等的部分,然后递归下去,达到一定深度或某种限定条件后停止分割。
如果一个元素与父节点不相交,那么它与子节点必然不会相交,那就不必与子节点进行相交运算了,从而达到加速的目的。四叉树可以用来加速视锥裁剪、相交检测。
那么给定一个场景,尝试来建立它的四叉树。
我们会面临几个问题。
- 物体是有包围体积的,它与节点的边界相交,应该如何处理?
- 物体在边界来回移动,如何处理?
- 建立四叉树的终止条件是什么?怎么做更好?
第一个问题,物体是有包围体积的,它与节点的边界相交,应该如何处理?
一般有两种解决办法:
- 所有与物体相交的叶子结点都存储该物体。
- 令非叶子结点能存储物体。
第1种方法的比较精确,但是问题在于大体积的物体会被非常多的子节点引用,管理麻烦,索引效率也比较低。
第2种方法比较常用,但是如果物体的包围盒与场景中心相交,那么即使再小的节点,也会被放到根节点中。而要解决这个问题,可以用松散四叉树。当划分空间时,将这个空间适当扩大,以容纳更多的物体,这样很多物体会被尽可能的子节点容纳,从而提高索引效率。
第二个问题,物体在边界来回移动,如何处理?
最简单的方法是,只要移动了就将它从四叉树中删掉,然后再重新添加即可,当然这不好。可以检查新位置与旧位置是否在同一个叶子结点的范围内,如果是就不需要做重新插入的操作。
在上面提到的松散四叉树的结构下,结点有内边界(inner boundary)和外边界(outer boundray)。
检查物体是否已越过之前的外边界,如果已越过再检查进入了哪个结点的内边界,就可以知道如何更新物体的节点了。松散四叉树由于外边界的存在,也减少了移动的物体需要更新节点的次数。
另外物体被从原节点删除时,它从在的节点已经没有其他物体了,是否要删除这个节点呢。如果当物体被删除时,同时立即删除节点时,可能会面临马上就需要重新创建这个节点的情况。可以设定一个时间,一段时间过后,仍无物体加入到这个节点中,那么就其删除。
第三个问题,建立四叉树的终止条件是什么?怎么做更好?
建立四叉树要考虑遍历、插入的时间消耗,以及内存空间的耗费。
最暴力的方法,建立满四叉树,对空间和遍历时间都有很高的浪费。所以动态的创建结点,如果物体被插入到四叉树中,没有合适的结点存放,创建新的节点。再加上树深度的限制(4~7比较合适),可以大大减少节点的数量。
当然还有优化的空间,在下面这种情况下,会有很多的叶子节点只存放了1个物体。可以给节点加一个容量的概念,当节点被插入的物体数量大于容量时,分裂这个节点,也就是创建子节点,将物体插入到合适的节点中。
其他
八叉树原理同四叉树是一致的,只不过需要拓展到三维空间,更适合有高低起伏的场景。
知乎上有提到更好的空间划分方法——VDB,待研究。
GitHub - AcademySoftwareFoundation/openvdb: OpenVDB - Sparse volume data structure and tools