[教學視頻]計算機圖形學基礎 在線學習教程
*原創文章,轉載請註明出處*
利用OpenGL點精靈實現雲模擬
Cloud Simulation Using OpenGL PointSprite
這篇教程介紹一種利用OpenGL點精靈,也就是PointSprite來實現雲的模擬。在網上找了一些資料和論文,都是模擬真實的雲和光照,且都比較複雜。後來看到一篇文章,介紹了一篇相對簡單但效果比較好的方法。首先建立好雲的輪廓模型,可以使用3DMax或其他的建模工具,然後創建該模型的包圍體,將包圍體中屬於雲的部分抽取出來,最後對抽取出來的部分進行三角化,然後貼圖,最後加上光照。受到該方法的啓發,這裏我介紹一種更爲簡單的方法來實現對雲的模擬。
和從文件讀取雲模型相比,用代碼生成雲的輪廓要快的多。雲的輪廓我們可以用球來模擬,使用多個不同大小的球可以組成不同形狀的雲。圖Fig1中顯示了利用三個半徑不同的球組成的雲的形狀。當然你也可以使用更多不同大小的球來組成更逼真,更復雜的雲。
|
Fig1. 不同大小的球組成的雲的形狀 |
描述這樣三個球只需用幾個結構體即可。
struct VECTOR3 { float x,y,z; VECTOR3(){} VECTOR3(float _x, float _y, float _z) { x = _x; y = _y; z = _z; } }; |
結構體VECTOR3是一個三維向量結構體,用來表示球的圓心。再加上半徑R我們就可以描述球的幾何性質了。於是球的結構體可以定義爲
struct Sphere { VECTOR3 C; float R; Sphere(){} Sphere(VECTOR3 c, float r) { C.x = c.x; C.y = c.y; C.z = c.z; R = r; } }; |
接下來就要得到整個雲的包圍盒,包圍盒可以用包圍盒立方體對角線上的兩個頂點來描述,這裏使用頂點座標全爲負和全爲正的兩個點。包圍盒機構體可以定義爲
struct BoundingBox { VECTOR3 origin; VECTOR3 MinPt; VECTOR3 MaxPt; Line Boundaries[12]; }; |
在上面的結構體中,origin表示包圍盒的中心座標,MinPt表示全爲負的頂點,MaxPt表示全爲正的頂點。利用球心所在位置和球半徑就可以決定包圍盒的大小。用每一個球的球心位置減去該球的半徑,然後比較這個值,最小的就是MinPt的值。同樣用每一個球的球心位置加上該球的半徑,其中最大的就是MaxPt的值。爲了方便後面繪製該包圍球,該結構體中還有一個Line結構體,該結構體表示包圍盒的邊的信息,定義如下
struct Line { VECTOR3 origin; VECTOR3 end; };
|
很明顯Line結構體中origin和end分別表示包圍體一條邊的起點和終點。Fig2顯示了Fig1中雲形狀的包圍盒。
(a) |
(b) |
|
Fig2. 雲的包圍盒
爲了用PointSprite來模型雲,我們需知道每個PointSprite在三維空間中的位置。而這些位置就是由雲的形狀來決定的。只要能找到每個球內的PointSprite,那麼就能利用PointSprite來模擬雲的形狀了。這裏用的方法有點體渲染(Volume Rendering)的味道。先將包圍盒分成一個一個的體素(voxel),voxel的多少由voxel的大小來決定。現在要做的就是抽取出所有球內的voxel。
抽取的過程很簡單,只要判斷當前voxel和球的位置關係即可。將voxel的座標帶入球的方程(已知球心和半徑),判斷和半徑的關係,只要小於等於半徑的平方,那麼該voxel就在球內。
(x-a)2+(y-b)2+(z-c)2 ≤ R2
上式中(a,b,c)爲球心,R爲半徑。
(a) |
(b) |
(c) |
(d) |
Fig3 從包圍盒中抽取雲輪廓內的體素
從Fig3中可以看到,(a)中顯示了包圍盒的所有體素,(b)中抽取出了在雲形狀內部的部分體素。(c)中可以清楚的看到雲內部體素的形狀,而(d)則是將雲內部每個體素的位置加上一定隨機擾動的結果。
有了雲內部voxel,就可以將每個voxel當作一個PointSprite,適當的調節PointSprite的大小和體素的大小,然後給PointSprite貼上帶alpha通道的紋理即可,如Fig4。
(a) |
(b) |
|
Fig4 PointSprite模擬的雲
整個雲模擬的過程中,重要的部分就是將包圍盒中用於模擬雲的球內部的體素抽取出來。只要正確的抽取出雲內部的體素,那麼雲的形狀也就得到了。具體的內容是實現方法可以參考該教程的源代碼。如果OpenGL版本不支持PointSprite,請更新OpenGL驅動,並下載高版本的OpenGL庫。
*原創文章,轉載請註明出處*