BVH with SAH (Bounding Volume Hierarchy with Surface Area Heuristic)

 
- BVH with SAH (Bounding Volume Hierarchy  with Surface Area Heuristic) - 
 
 
0. Overview
包围层次盒(BVH)是一种基于"物体“(object, 区别于“空间”,spatial )的场景管理技术,普遍应用于碰撞检测、射线相交测试之类的应用场合中。
通常状况下BVH的性能都比较不错,起码比基于格子(Grid)的场景管理技术要高。
 
BVH的数据结构其实就是一棵二叉树(Binary Tree)。它有两种节点(Node)类型:Interior Node 和 Leaf Node。 前者也是非叶子节点,即若是一个Node不是Leaf Node,它一定是Interior Node。Leaf Node 是最终存放物体/们的地方,而Interior Node存放着表明该划分(Partition)的包围盒信息,下面还有两个子树有待遍历。
 
使用BVH须要考虑两个阶段的工做:构建(Build)和遍历(Traversal)。
 
1. Build
构建工做考虑的是如何构造一棵能够有效描述当前场景信息的二叉树。这当中的关键是如何对(假定)毫无规律地散落在场景中的众物体进行划分(Partition),即决定哪些物体该划分到左子树上,哪些物体该划分到右子树上。咱们能够把这个问题抽象成一个”划分策略(Partition Strategy)“——咱们总会按照某种”策略“划分场景的,待会再考虑具体有哪些策略。另外,因为咱们是在3D空间中工做,为了将问题简化,用分而治之的角度看,咱们能够首先创建一个”原则“(Principle):即决定在哪根轴(x,y,z)上进行划分。”原则“(Principle)与”策略“(Strategy)的不一样之处在于,无论用何种”策略“,老是遵照同一种”原则“。
 
1.1 Principle
决定在哪根轴(x,y,z)上进行划分,取决于场景中的物体在各个轴上分布的“散度”。若是这些物体沿着某根轴分布得最为“松散”(即落在该轴上靠一侧最近的物体与另外一侧最近的物体,两者距离为最大),那么就沿该轴进行划分。
 
1.2 Strategy
肯定了以哪根轴进行划分,接下来就要考虑“怎么划分”。这时有多种策略能够考虑: pbrt中应用到的有三种划分策略:取中点划分(SPLIT_MIDDLE),按等量划分(SPLIT_EQUAL_COUNT),以及用表面积启发式算法划分(SPLIT_SAH, SAH=Surface Area Heuristic)。
 
1.2.1 SPLIT_MIDDLE
顾名思义,取中点划分的意思就是在先前选取的轴上找到最靠两侧的物体的包围盒,链接两者包围盒的重心获得一条线段,取其中点做为划分点,中点以左划分到左子树,中点以右划分到右子树。顺便提一下使用std::partition算法能够快速实现这一目的。
 
这种划分实现起来最为简单,但每每效果不是太好:由于物体的分布每每不是均匀的。其中一种糟糕的状况(a)是,某侧子树上可能会拥挤过多的物体,而另外一侧子树上却太少。这对查找效率影响很大:想象一种极端状况,对于有n个物体的场景,经过这样的划分可能会有n-1个物体落在了左子树,而1个物体落在了右子树。这种状况下BVH的管理技术基本等于形同虚设,解决不了实际问题。
 
另外还有一种糟糕的状况(b),就是包围盒之间互相“重叠”(overlapped)的状况。若是两棵子树对应的包围盒“重叠”的越多,那么一条射线穿过该区域时同时击中两子树的几率也就越大,这就意味着两棵子树都得进行相交测试。若是全部的子树都不得不进行相交测试,那便失去了“场景管理"本该具备的做用。
 
1.2.2 SPLIT_EQUAL_COUNT
按数量平分两棵子树,即左子树拥有的物体数量与右子树拥有的物体数量相等。使用std::nth_element算法能够快速实现。
 
此种策略是对第一种”按中点划分“策略的改进,对于处理(a)状况有很大的改善。可是对于(b)状况,依然一筹莫展。若是遇到(b)状况出现,使用该策略性能也是很是低下的。
 
1.2.3 SPLIT_SAH
好东西老是留到最后面讲。表面积启发式算法(Surface Area Heuristic)应该是目前应付以上(a)(b)状况最好的算法了,同时也是性能最高、成本最低的算法。该算法基于的理论是复杂度成本分析和几率论,这种同时涉足了数学与计算机科学的交叉理论使得它披上了一点高大上的光环。
 
从复杂度成本分析的角度看,假设场景中有1,2,...n个物体,若是不作场景管理的话,那就须要射线对每一个物体都作相交测试。假设对于第i个物体,其作射线相交测试的时间复杂度为t(i),那么整体的时间复杂度就为t(1)+t(2)+...t(n) = ∑t(i)。这个值固然越小越好。t(i)是个相对值,表明所用的时间复杂度,并非真要求这个时间仍是咋地。能够简单设这个值为单位1。 pbrt中,有一个简单的假设是对每一个物体进行射线相交测试所用的时间复杂度都是相等的,皆为1。因此∑t(i)能够用n来代替。
 
再假设这样一种划分方式:将场景划分为两个区域A和B,物体散落在AB两区中。射线有可能会击中A区,也可能会击中B区。在遍历前咱们固然没法断言射线究竟会击中哪一个区。但毫无疑问,击中哪一个区,就要遍历散落在该区内全部的物体,∑t(i) in A or B。
 
射线究竟会击中A区仍是B区,虽然没法断言,却能够”猜想“。这就用到几率论的理论了。假设击中A区的几率为p(A),击中B区的几率为p(B)。那么综合以上的分析,就会获得整体的时间复杂度为
 
cost(A,B) = p(A) *  ∑t(i) in A  + p(B) *  ∑t(j) in B
                = p(A) *  n in A  + p(B) *  m in B
其中n为A区下的物体个数,m为B区下的物体个数
 
我我的的一点理解,以为这玩意难道不就是数学中的指望么?(书中却是没提这茬)。同理,这个值越小越好,也就是说所求得的指望值越低越好(总体上复杂度的平均值降到最低,即是最优解。这是我的的一点揣测)。
 
那么,p(A)和p(B)怎么算呢?这时就要用表面积估算了。这根植于一个朴素的想法:若是某物的表面积越大,那么它被射线击中的可能性也就越大。举一个简单的例子,假设空间中有一个6面立方体,一条射线从中穿过。问击中其中一个面的几率是多少(1/6)?击中其余五个面呢(5/6)?这种状况下实际上能够简单把表面积的比率(ratio)看做是被射线击中的几率。面积比越大,被击中的几率天然越大。我见到过图形学中不少算法,都有相似的作法。
 
具体到这个算法中,表面积是经过节点的包围盒的表面积(即长方体的表面积)求算的。仍然考虑一个父节点(C)下带有左(A)右(B)子节点(子树)这种状况。父节点C的表面积为S(C),左子节点A的表面积为S(A),右子节点B的表面积为S(B)。那么击中父节点下的左子节点A的几率为p(A|C) = S(A)/S(C),击中父节点下的右子节点B的几率为p(B|C) = S(B)/S(C)。
 
 
注意S(A)/S(C)+S(B)/S(C) 是有可能大于S(C)的,由于S(A)与S(B)可能会发生”重叠“,这固然是咱们不喜欢的状况,缘由上面已经讲过。因此S(A)/S(C)+S(B)/S(C)越小(越接近S(C))咱们越喜欢。
 
在划分一个节点表明的空间区域时,能够经过不一样的切法将该空间划分红两个子区域。切法能够平均等距地一刀一刀地切,也能够耍点小手段带点”智能“使其”自适应“。总之每次划分,都会获得两个子区域A和B。那么相应也会算出一个cost(A,B)来。比较全部划分方案下所得的cost(A,B),取值最小的方案就是成本最低的划分方案,也做为划分该节点的最终/优方案。以下图所示,scheme1和scheme2就是两种不一样的划分方案。
 
 
反思sah这种算法,想一想为何它可以有效应付糟糕状况(a)和(b)?个人理解是它综合考虑了”分布“(体如今因子∑t(i)=n)和”重叠“(体如今因子S(A)/S(C)=p(A|C))两种状况,两者的乘积最小,表明检测它所花的成本最小(咱们固然喜欢某个区域内需待检测的物体越少越好,且该区域与其余区域发生的”重叠“也越小越好了)。但这只是个人胡思乱想,没有数学证实能够支持这一分析。
 
sah的作法并不能彻底作到”不重叠“或者使划分后的分布就很“均匀”,但它每作一次划分,选取的都是当前情形下最优的方案。所以称它是一种“启发式”(Heuristic)算法。
 
1.3 Compact BVH
构建好一棵BVH后能够进一步优化。树的结构是经过指针寻址下一个子节点的,与连续空间的数组存放数据的方式相比,不管内存的整齐程度仍是遍历的速度都逊色很多。那能不能把这棵BVH转换成一个数组呢?答案是确定的,并且也不难。按深度优先顺序把遍历的节点包装一下(加点offset信息)依次放入数组中,就把这棵BVH“压平”到一个数组中。这样遍历起这个数组那速度可就快多了。
 
 
 
2. Traversal
遍历BVH差很少是件直截了当的事情。只不过这里注意下若是作了Compact BVH优化的话,实际上是对一个数组进行遍历,这时要经过算好offset的值来找到对应的节点(或是叶子)。
 
pbrt介绍了一种更加快速判断射线与包围盒相交的算法,但我没有细看。这里标记一下,留做之后研究。
 
另外要考虑的一个问题是,当发现射线与某个子节点相交的话,那么有无必要再检测下与另外一子节点是否相交?答案是要的。由于两个节点没法保证彻底“不重叠”,以下图所示,颇有可能在检测另外一子节点时发现了更近(closer)的交点。
 
 
还有一个问题是,当须要判断射线是否与子节点相交时,应该先检测左子节点呢仍是右子节点?答案取决于射线有可能会先与谁相交。若是射线经过的方向是从左到右,那就应该先检测左子节点,反之就应该先检测右子节点。由于上面讲过两棵子节点都要检测(由于可能“重叠”),经过这种方法能够提升检测效率。由于若是不重叠的话,当判断到另一棵子节点时就会当即返回了(不重叠的话就不可能有比当前相交点更近的值)。 以下图所示。
 
 
 
 
 
 
  ---
(完)
相关文章
相关标签/搜索