通過粒子系統來實現火焰效果,基本思想是把一團火焰看成是由一顆顆有其生命期的粒子組成,粒子在不停的產生直至消亡從而產生升騰的火焰效果。通過生成每個粒子的座標,每顆火焰粒子是一個矩形,而這個矩形由兩個三角形構成,包含6個頂點,把座標傳入渲染管線進行GPU的渲染生成最終效果。本文章效果是修改自《OpenGL ES 3.x遊戲開發》中的火焰效果。
##1、火焰產生的基本原理 1.1、每個粒子本質上是一個矩形,通過矩形圖案中的不透明區域來確定粒子的形狀。這裡實現效果使用的是不透明區域為圓形的矩形。
1.2、產生火焰效果的粒子的位置不是固定的,是在一定區域內隨機產生的,通過控制粒子的運動方向和生命週期產生火焰的形狀。 1.3、粒子運動過程中還需要動態改變顏色值,著色器接收渲染管線傳入的起始顏色、終止顏色、總衰減因子。然後根據當前片元距離粒子紋理矩形中心點的距離、總衰減因子、片元紋理取樣顏色的透明度通道值、起始/終止顏色計算出當前片元的顏色,結合矩形中的透明度展示最後的顏色。##2、具體的實現 #####2.1、Java層主要的構成物件
- 2.1.1 CustomGLSurfaceView:繼承自GLSurfaceView,專門負責OpenGL ES3.0的渲染和觸控事件的處理;
- 2.1.2 SceneRender:CustomGLSurfaceView的內部類,實現GLSurfaceView.Renderer,CustomGLSurfaceView的渲染就是通過其實現的;
- 2.1.3 ParticleSystem:火焰粒子系統的總控制類 ParticleSystem,實現了對所有粒子位置的計算以及該位置所對應的 6 個頂點座標值的計算,同時還實現了定時更新粒子位置以及根據攝像機位置計算火焰朝向等;
- 2.1.4 ParticleForDraw:實現粒子的繪製;
- 2.1.5 ParticleDataConstant:常量類,封裝了四個火焰效果的位置,顏色,生命週期等資訊;
- 2.1.6 MatrixState:工具類,實現矩陣變換;
- 2.1.7 LoadUtil:工具類,用於3D模型檔案載入,因為本案例沒有使用到obj格式的3D模型,所以是沒有實現的;
- 2.1.8 ShaderUtil:工具類,用於載入頂點著色器和片元著色器(OpenGL ES 2.0之後已經擯棄固定管線,現今是通過著色語言實現可程式設計管線功能);
- 2.1.9 TextureUtil:工具類,用於載入圖片資源轉化為紋理供渲染管線使用; #####2.2、渲染管線著色器的構成 從下圖中可以看出頂點著色器和片元著色器所處的位置。 在這個效果實現中也得必須實現這兩個這色器。 著色器是使用著色語言編寫的,在GPU中實現,是可程式設計管線中的重要組成部分,主要分為頂點著色器和片元著色器。
- 2.2.1 vertex.sh:頂點著色器,是一個可程式設計的處理單元,其功能是執行頂點的變換、光照、材質的應用與計算等頂點的相關操作,每頂點執行一次。
#version 300 es
uniform mat4 uMVPMatrix; //總變換矩陣
uniform float maxLifeSpan;//最大允許生命期
in vec4 aPosition; //從渲染管線接收的頂點位置屬性
in vec2 aTexCoor; //從渲染管線接收的紋理座標
out vec2 vTextureCoord; //用於傳遞給片元著色器的紋理座標
out vec4 vPosition;//用於傳遞給片元著色器的頂點位置屬性
out float sjFactor;//用於傳遞給片元著色器的總衰減因子
void main()
{ //主函式
gl_Position = uMVPMatrix * vec4(aPosition.x,aPosition.y,0.0,1); //根據總變換矩陣計算此次繪製此頂點位置
vTextureCoord = aTexCoor;//將接收的紋理座標傳遞給片元著色器
vPosition=vec4(aPosition.x,aPosition.y,0.0,aPosition.w);//計算頂點位置屬性,並將其傳遞給片元著色器
sjFactor=(maxLifeSpan-aPosition.w)/maxLifeSpan;//計算總衰減因子,並將其傳遞給片元著色器
}
複製程式碼
- 2.2.2 frag.sh:片元著色器是用於處理片元值及其相關資料的可程式設計單元,其可以執行紋理的取樣、顏色的彙總、計算霧顏色等操作,每片元執行一次。
#version 300 es
precision mediump float;//給出預設浮點精度
uniform vec4 startColor;//起始顏色
uniform vec4 endColor;//終止顏色
uniform float bj;//紋理矩形半徑
uniform sampler2D sTexture;//紋理內容資料
in vec2 vTextureCoord; //接收從頂點著色器傳過來的紋理座標
in vec4 vPosition;//接收從頂點著色器傳過來的片元位置屬性
in float sjFactor;//接收從頂點著色器傳過來的總衰減因子
out vec4 fragColor; //輸出到的片元顏色
void main()
{ //主函式
if(vPosition.w==10.0)
{//該片元的生命期為10.0時,處於未啟用狀態,不繪製
fragColor=vec4(0.0,0.0,0.0,0.0);//捨棄此片元
}else
{//該片元的生命期不為10.0時,處於活躍狀態,繪製
vec4 colorTL = texture(sTexture, vTextureCoord);//進行紋理取樣
vec4 colorT;//顏色變數
float disT=distance(vPosition.xyz,vec3(0.0,0.0,0.0));//計算當前片元與中心點的距離
float tampFactor=(1.0-disT/bj)*sjFactor;//計算片元顏色插值因子
vec4 factor4=vec4(tampFactor,tampFactor,tampFactor,tampFactor);
colorT=clamp(factor4,endColor,startColor);//進行顏色插值
colorT=colorT*colorTL.a; //結合取樣出的透明度計算最終顏色
fragColor=colorT; //將計算出來的片元顏色傳給渲染管線
}
}
複製程式碼
##3、總結 效果看起來挺簡單,但是需要的知識點特別多,示例中有一些地方我理解得也還不是很充分,因為其實現過程比較繁瑣,粒子的頂點需要一個個繪製,幸虧是原有的示例,只是在其基礎上進行一些簡化和修改,新增了一些註釋,示例原始碼可以拿來學習一下ParticleFireProject,在專案中是不能直接使用,因為其還有很多優化點。