在初試PyOpenGL一 (Python+OpenGL)講解Pyopengl環境搭建,網格,球體,第一與第三人稱攝像機的實現。在初試PyOpenGL二 (Python+OpenGL)基本地形生成與高度檢測 里以用高程圖生成地形以及以球體做三人稱漫游。初試PyOpenGL三 (Python+OpenGL)GPGPU基本運算與乒乓技術 里實現了基本的GPGPU運算。
我認為比較完善的GPU粒子系統應該如下,粒子初始化可以放在CPU里,但是相關數據運算首先要放在GPU里,並且運算后的數據也應該放在顯存里,而不是內存里。故用第三篇實現GPU粒子系統不滿足,因為他數據是存放在紋理中,要放入VBO里,必需先讀取經過內存,然后存放入顯存里,這里雖然運算是放入GPU了,但是數據要經過顯存-內存-顯存的過程,產生不必要的消耗,並且,因為數據是存放在紋理的像素里,故限定在片斷着色器中,這二個限制導致第三篇里的內容不能用來實現GPU粒子系統,而是用來實現一些需要結合CPU與GPU結合處理的運算。
在這里,我們采用OpenGL 里的Transform Feedback,和第三篇采用FBO結合浮點紋理不同,Transform Feedback簡單來說,傳入一個VBO,經過GPU運算后,放入另一個VBO中,注意二點,操作都是針對VBO,也就是針對顯存,故不需要經過CPU與內存,還有一點就是在Transform Feedback里,一個緩存不能同時作為輸入和輸出。
首先來看一下簡單的例子介紹Transform Feedback的基本應用,首先指出一點,GLSL3.0與GLSL4.0的Transform Feedback寫法有些區別,手上分別有支持3.0與4.0的顯示,但是為了更好的兼容性,選擇3.0的寫法,相應代碼和着色器代碼如下:



着色器里代碼很簡單,傳入一個float數據,返回二個float數據,上面我們傳入一個數組,[1.0, 2.0, 3.0, 4.0, 5.0],經過着色器里簡單運算,分別返回這個數據加3值,與一個固定值1.0.然后在transformFeedback我們為了驗證正確與否,需要讀取VBO里的數據。在這里,pyopengl可以使用glGetBufferSubData與glMapBuffer來得到VBO里的數據,需要注意的是,python與c之間的一些指針,數據的轉換,引入ctype,聲明ctype類型的數組,然后轉換成對應的指針,填充這個數組后,然后轉換把指針轉化成numpy里的數組.得到的數據如下:
可以看到,傳出的數據是4,1,5,1,6,1,7,1,8,1,對比傳入的是1.0, 2.0, 3.0, 4.0, 5.0。驗證正確。
下面我們以上面的例子來實現我們的粒子系統,這里先入相關Python代碼。

結合前面的例子和上文中的乒乓來看,粒子在這里我們每個定義七個數據,前三個用來表示他的位置,后三個用來表示他的速度,最后一個用來表示他在顯存里的存活時間。在update就是把數據從一個緩存經過GPU運算放入另一個緩存的過程,例如第一楨,我們傳入fvbo,然后數據輸出到svbo.在第二楨里,數據就從svbo經過GPU傳入到fvbo,第三,第四分別如第一,第二。這樣就能實現如第三篇中的乒乓技術。然后在顯示render里,我們就用當前輸出的緩存里的數據簡單的輸出顯示,本文只是介紹用法,實現如雪花,雨滴,瀑布等特效需要對相關初始化粒子,着色器代碼,添加紋理做更改,但是基本處理還是如上。
下面是着色器代碼,實現粒子與球的碰撞,也有與地面的交互。代碼如下:

整個過程比較簡單,也只考慮一些基本的碰撞,比如球的速度也應該影響碰撞后粒子的方向,但是這里只考慮粒子碰撞球后反射的方向,與地面的碰撞后,不會反彈,會慢慢停止向前移動。
最后一些相關着色器的參數設置代碼。

在本文中,試着用了5千W個粒子,發現初始化很慢,花了十幾秒,但是楨數和5000個粒子基本沒有差別,從這里可以看出,GPU並行處理的強大之處。
完整代碼:PythonGPU粒子系統.zip 操作方式EDSF前后左右移動,WR分別向上與向下,鼠標右鍵加移動鼠標控制方向,V切換第一人稱與第三人稱。UP與DOWN切換前面操作的移動幅度。