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)的交點。
 
 
還有一個問題是,當需要判斷射線是否與子節點相交時,應該先檢測左子節點呢還是右子節點?答案取決於射線有可能會先與誰相交。如果射線通過的方向是從左到右,那就應該先檢測左子節點,反之就應該先檢測右子節點。因為上面講過兩棵子節點都要檢測(因為可能“重疊”),通過這種方法可以提高檢測效率。因為如果不重疊的話,當判斷到另外一棵子節點時就會立即返回了(不重疊的話就不可能有比當前相交點更近的值)。 如下圖所示。
 
 
 
 
 
 
  ---
(完)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM