基於SPH的流體模擬實踐和一些技巧總結

轉載自:http://blog.sina.com.cn/s/blog_6f638fb60100shw0.html

 

        SPH的流體模擬是目前大多數遊戲所採用的模擬流體方法,特點是簡單,十分容易實現,相比與基於Grid的Eulerian方法更加簡單和高速,本文主要介紹一下使用SPH的流體模擬中一些常用的技巧和數據結構。


目前流體模擬中常用的2類方法

        分別代表了從2種不同的方面來解釋Navier-Stokes的流體方程:

   1、Eulerian方法從空間固定點觀察該點的值得變化。

   2、Lagrangian方法則將液體看作是跟隨着流動的Particle。


Eulerian方法比較複雜,常用作離線模擬,可以產生非常逼真的流體效果,具體可以參考SIGGRAPH 2007的《FLUID SIMULATION》,作者是Bridson 和 Muller。

現在遊戲中常用的實時流體模擬通常使用Lagrangian的方法來模擬流體,也就是基於粒子的方法,而大多數採用的都是基於SPH(Smooth Particle hydrodynamic)的方法。主要參考文章爲Muller的《Particle-Based Fluid Simulation for Interactive Applications》。



SPH方法非常簡單和容易實現,從我自己做過的剛體和柔體模擬的經驗來看,可以說基於SPH的流體比剛體和柔體的模擬簡單和容易實現的多。


SPH方法概括的說:

1:對每個粒子,通過它附近周圍的所有和它的距離半徑小於r的所有粒子來計算出該粒子點的密度r被稱爲Smooth Length,通俗的說就是每個粒子的最大影響半徑。

2:對於每一個粒子,通過理想氣體狀態方程根據上一步計算出的密度從而算出它的壓強。

3:對於每一個粒子,根據它附近周圍距離半徑小於r的所有粒子的壓強(上一步已經計算出)來計算出該粒子由於附近壓強差而導致受到的壓力差。

4:對於每一個粒子根據它附近周圍距離半徑小於r的所有粒子的速度差來計算出該粒子由於附近速度差而導致受到的粘滯力。



對於第一步求解密度來說,
其中r代表粒子i和粒子j之間的距離,h代表前面說的Smooth Length

對於第二步來說使用公式 其中 是粒子的密度 是流體的穩定密度比如水的話一般取1000kg/立方米。



   第3,4步也可以依次類推。具體請參考Muller的文章《Particle-Based Fluid Simulation for Interactive Applications》。



實現中的常用技巧


下面是一些實現中的常用技巧,希望對你有所幫助。


注意,根據上面一片文章的SPH方法,需要推導Smooth Kernel函數的梯度和拉普拉辛算子。但Muller在文章中只給出了3個Kernel(一個計算密度,一個計算壓強差力,一個計算粘滯力)函數,並沒有幫我們推導出後2個函數的梯度公式和拉普拉辛算子公式。一開始我還花時間自己推導了下,還反覆檢查生怕推導錯了(以前大學學數學我的粗心導致1,2個符號錯誤是常有的事情)。後來發現《SPH survival kit》這片小文章裏已經幫你都推導好了。。。汗。所以你只要照抄就可以了。



爲了快速定位一個粒子周圍的例子,我使用了空間Grid的數據結構,通常的說就是將空間等分成尺寸爲h的立方格,這樣對於一個粒子來說只要查找周圍3X3X3的立方格中所包含的粒子就可以了。



爲了實現無限大範圍的Grid系統,我使用了空間稀疏哈希的方法來存儲Grid,這是因爲空間大部分Grid都是空着的所以使用Hash表存儲和快速定位那些包含粒子的Grid,具體可以參考《Optimized Spatial Hashing for Collision Detection of Deformable Objects》。


每對粒子之間的力只需要計算一次就可以了(這樣提高2倍的速度),也就是說對於粒子A,B來說。A粒子由於受到B粒子作用的壓強力分量,和B粒子由於受到 A粒子作用的壓強力分量是相同的只差一個正負號而已,爲了避免計算A和B所受的作用力時重複計算,我在每一楨開始模擬前,首先構造每個粒子的鄰接粒子列表:同時要保證A和B的鄰接關係要麼只出現在A的鄰接列表中,要麼只出現在B的鄰接列表中。具體做法很簡單,首先清空Hash Grid中每個Grid中的粒子信息,然後對於每一個粒子首先查找周圍27個鄰接Grid中的粒子,將這些粒子中和自己的距離長度小於Kernel Smooth Length的那些例子加入自己的鄰接列表,最後將自己加入到自己所在的Grid中,這樣就能保證A和B的鄰接關係出現並且只出現一次(將會出現在後面的那個粒子的鄰接列表中,因爲對於前一個粒子來說,在統計它的鄰接粒子的時候,後一個粒子還沒有被加入到Hash Grid中~~)。

實際上Muller給出的那些公式中有不少可以提取公因式的部分,你可以仔細去研究下會發現很多可以優化速度的地方。

水面信息抽取

轉載自:http://blog.sina.com.cn/s/blog_6f638fb60100shw0.html

上面的步驟只是完成了一個粒子系統的模擬,這樣你看到的還只是一堆水分子,在正式渲染的時候需要通過第一步計算出的密度場信息來構造出水錶面。我這裏也是採用Muller論文裏的Marching Cube算法,這個算法簡單容易實現,但是效率極低,我模擬1500個粒子在一臺Core2  4400的Intel cpu上可以達到150fps,一旦經過Marching Cube算法後一下子掉到52fps.所以不大推薦這種方法,我個人看到過一片比較好的文章是《Interactive Water Streams with Sphere Scan Conversion》,感覺挺不錯,作者號稱速度比Marching Cube快了很多倍。不過我自己也沒有時間去做。



Marching Cube算法的論文<<Marching Cubes:A High Resolutin 3D Surface Construction Algorithm>>,注意論文中提到的構造一個頂點信息構造表,這個表你可以自己寫程序構造。。但是更加方便的技巧是你去下載一個叫做 “PolyVox”的開源體素構造庫,它裏面已經幫你把表格生成好了。



水體渲染

基本上需要模擬的包括水對於背景的折射效果,水面的Specular ,以及菲涅爾效果。其中前2項是最重要的,折射我使用的方法是先渲染水體以外的所以其他場景物體,然後將該Back Buffer拷到一張渲染紋理上,然後在渲染水的時候在PS裏面對這張紋理進行一些扭曲(根據法向量)。最後將像素點寫回back buffer,同時根據計算出的菲涅爾係數將像素點的顏色值和反射顏色值進行融合。(這個折射方法是同事教的,正好他在做畫面扭曲效果。。)Specular的話和一般的Specular計算方法相同。,下面是幾張截圖:

這是模擬將液體注入一個盒子的過程
1.剛開始注入
注入中
注入液體一段時間後
注入完畢
靜止後

總結

SPH流體模擬主要包括3部分:

1、水分子模擬

2、水面信息抽取

3、水體渲染

其中第一部分,第三部分比較簡單,但是好的快速的效果需要有良好、真實地水面信息抽取算法支持。Marching Cube算法可以通過加大網格精度來不斷提高視覺效果,速度卻得不到保障,所以需要一個更加出色的算法來解決,因此如何開發一個高效、真實的適用於流體的Surface Extraction算法會是個很大的挑戰。

發佈了95 篇原創文章 · 獲贊 34 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章