Tetrahedron based light probe interpolation(基於四面體的Light Probe插值)

在當前的遊戲引擎中,使用Light Probe來計算全局環境光對於動態物體的影響是一種很主流的方法。在預處理階段生成完場景的Light Probe之後,傳統的方法採用查找最近的8個相鄰的Probe然後使用三線性的方式(Trilinear Interpolation)進行插值,但是這樣的插值代價稍大,不過一個可行的優化就是儘可能地減少插值中使用的Probe的數量,比如由8個減少到4個不等。但是這時就不能再用三線性插值,而需要用其它的插值方法比如Inverse Distance等,不過這樣就會帶來另外一個問題,那就是使用的Probe一般是最近的幾個(如三個到四個),如此一來當物體移動之時得到的最近Probe變化可能會比較明顯,進而得到的插值結果也會不太連續,影響效果。今年的GDC上Unity的一篇Presentation提到了使用四面體來進行無序Probe的插值,稍花了些時間簡單實驗了下,還是蠻不錯的。文章可以在這裏看到:Slide,其最大的一個特點就是Probe可以無序地隨機分佈,這樣在編輯器裏邊添加以後對美工還是蠻友好的。

四面體網格的生成

爲了對Probe所對應的點集使用基於四面體的插值,首先就需要生成這堆點集所對應的四面體集合,這個問題屬於傳統幾何圖形學的典型問題,解決方法也很多,比如廣泛用在三角細分算法中的Delaunay方法(Paper)。使用Delaunay的方法可以實現對三維空間點集的四面體化操作,不過鑑於此操作不是所關注的問題的關鍵所在,可以藉助於已有的代碼來完成該步驟。比如這裏就有一個開源的代碼庫:TetGen(作者應該是一個身在德國的中國哥們)可以完成點集的四面體切分,當然這個裏邊還有其它不少更高級的功能,感興趣也可以學習一下。它支持輸入很簡單的點集描述,然後在一些簡單的配置參數下計算得到該點集的四面體化結果。比如對一堆隨機生成的點集的四面體化效果如下所示:

四面體重心座標的計算

要使用四面體對一個給定的目標點進行插值,就要計算其相對於該四面體的重心座標,也即是其相對於四個頂點的權重比值。這個操作要在每次Probe進行更新的時候都需要進行,因而就要求效率儘可能地高,之前的blog中介紹了使用有向平面距離的方法來計算四面體重心座標的方法,效率還可以。

其中,這裏邊的有向距離1 / D(a,PLbcd), 1 / D(b,PLacd), 1 / D(c,PLabd), 1 / D(d,PLabc)對於每個四面體來說均是固定的,因而就可以存儲到每個體數據中以便加快效率。

空間數據結構的組織

由於常規的Probe分佈是由對應的AABB或OBB來進行定位的,這樣在查找目標點所對應的鄰近Probe時可以直接進行對應。因爲一個區域內的Probe一般是在三個方向上均勻分佈的,也就相當於一個三維的數組結構,這樣給定一個目標點的位置,就可以直接根據Probe的密度、各個方向上的size及step直接定位到其周邊的相鄰Probe (AABB 的操作最直接,對於OBB則需要做一個局部座標系的轉換,之後就跟AABB的計算相同),因而這種情況下對於整個Probe數據的組織就比較簡單。但是使用基於四面體的方式之後,由於原始Probe的無序,因而這裏的數據結構組織就較常規的方法有些區別。首先,對於四面體組織的網格,需要知道四面體間的拓撲信息,也即通過一個四面體能夠找到其鄰接的四面體,如果用ID來對應每個四面體的話那麼在每個四面體中就需要添加四個存儲其相鄰體的ID數組。另外,由於四面體插值的頻繁進行,而這裏的插值方法又是使用面的方程來進行,因而就可以將每個面的描述信息也進行存儲,這樣來提高效率。但是四面體的面是可以公用的,雖然一個面最多隻能被兩個四面體共用,但還是有必要在存儲前來刪除冗餘。這樣其實最終的數據結構就可以如下描述:

  • 點:點的位置;關聯的Probe的ID或指針;
  • 面:關聯的三個點的ID;面方程;
  • 體:關聯的四個面的ID

當然,在上述描述的具體實現過程中還會有其它若干細節,比如要注意組成四面體的點與面之間的對應關係,以便正確找到下一個關聯的四面體等;不過這些都比較瑣碎且也是具體實現相關。

查找與插值的處理

由於在上述數據結構中存儲了四面體之間的拓撲結構,因而在進行目標點對應的四面體定位時就可以使用這些拓撲信息。首先,對於點與四面體的關係及其對應的重心座標可知:如果一個點在四面體內部,那麼它對應的重心座標均屬於[0,1];否則,就可以通過重心座標的越界方式找到下一個其可能位於的四面體,如此進行若干次迭代之後就可以正確定位到其所在的四面體,或是到達所有四面體集合的邊緣。爲了快速定位一個點對應的四面體,可以有多種方法:

  1. 選擇所有四面體中處於較中心位置上的一個四面體作爲Spawn點,來進行目標體的查找(Presentation中使用的方法)
  2. 隨機選擇一個四面體作來初始點,來進行目標體的查找
  3. 使用各種空間分割方法,如BVH等來對四面體集進行空間組織後加速目標體的查找

當然,在使用上述方法的過程中又會有多種優化方式,比如一些信息的Cache,像記錄上一次的目標四面體作爲下次查詢的初始點等。

對於目標點位於四面體集合外側的情況,使用邊界擴展的方法來進行插值計算。如上圖所示(以2D情形爲例),其中的黑色線條爲體集合的外緣,而綠色區域爲每個三角面向外側的擴充;其中的綠色線條方向爲交界頂點處的法向量(這其實也相當於計算每個外表面對應的Voronoi區域)。然後,對於每個目標點P通過投影得到其對應的原始三角面中的位置P',以P'計算其對應三角形的重心座標後用此三角形上的三個點來插值得到P點的最終結果值。但是這種方法會在集合外側得到與到集合表面距離無關的插值結果,而實際情況中想要的應該是:外側體面對於目標點的影響應該隨着該點到面的距離增大而減弱。不過這個也比較簡單,可以對最終的外緣插值結果再增加一個針對距離d的衰減變化即可。

與常規方法的比較

最後,將基本四面體的Probe插值方法與傳統的插值方法進行對比。整體來說,這種方法還是不錯的,如果引擎中有之前的均勻分佈的Probe系統之後,修改到這種基於四面體插值還是比較容易的。由於Probe的均勻分佈,因而可以不錯助於其它方式(如TetGen)而直接生成其所對應的四面體網格,而對於數據結構的修改也不是很大。對於某一個物體記錄四面體Cache之後的查找更新操作也是比較快的。就算不帶來效率提升也不至於也不用損失太多的性能(具體數據還木有來得及對比)。不過使用基於四面體的方法之後最大的一個優勢就是可以在引擎中將此前以Probe集合爲基本單位的Probe系統轉化爲以單個Probe爲基本單位的新系統,Probe的生成與控制方式更加靈活,Unity裏應該就是這種方法。

其它的一些優化
  1. 在邊界插值時可以將距離變化影響也考慮在內,這樣只需針對d施加一個合適的衰減函數即可(線性或非線性的)。但是要注意此函數在d=0點處的值應爲1,以保證落在邊緣表面上的點的插值結果的正確性。
  2. 可以使用Least Square的方法並結合四面體的插值描述來對整個Probe系統進行優化,這樣在給定一定的誤差閾值之後,就可以減少Probe的數量。

下述爲一些實驗效果:其中的Probe是在一個指定的區域範圍內以均勻的分佈方式隨機生成,數量爲512個;Probe的球諧值採用球面蒙特卡洛採樣方式計算得到;每個Probe球面上採樣1024個點,場景採用BVH空間分割來加速求交測試:

其中的黑色線框即爲當前角色中心所處於的四面體,從中可以看出:兩個鄰近不同位置的目標點對應了兩個相鄰的不同四面體。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章