目錄
- 煙花爆竹場景和屬性
- 實踐以及遇到的問題
- 資料
- 收獲
通過該篇的實踐實現(xiàn)如下效果
一、煙花爆竹場景和屬性
在上一篇 音視頻開發(fā)之旅(15) OpenGL ES粒子系統(tǒng) - 噴泉 的基礎(chǔ)上 實現(xiàn)煙花爆炸效果。
在具體實踐之前宁仔,先來想一想坟瓢,煙花爆炸的場景和屬性
場景:從中心點開始爆炸卵洗,然后煙花粒子向各個方向炸開最铁,整體形狀也各有不同管跺,比如 北京奧運會的大腳印棵里,但是大部分還是圓形(因為設(shè)計實現(xiàn)相對簡單)润文,今天我們也實現(xiàn)一個圓形的煙花爆炸效果。
屬性:顏色殿怜、角度典蝌、運動矢量、形狀头谜。
實現(xiàn)流程和上一篇基本一致骏掀,不清晰整理的流程,建議先回看下
二柱告、實踐:煙花效果
在上篇的基礎(chǔ)上通過修改Render的onDrawFrame中的粒子發(fā)射器來逐步實現(xiàn)煙花爆炸效果截驮。
2.1 首先定義煙花爆炸對象
粒子的添加角度采用360度隨機的方式添加
public class ParticleFireworksExplosion {
private float[] mDirectionVector = {0f, 0f, 0.5f, 1f};
private float[] mResultVector = new float[4];
private final Random mRandom = new Random();
private float[] mRotationMatrix = new float[16];
public void addExplosion(ParticleSystem particleSystem, Geometry.Point position, int color, float curTime) {
Matrix.setRotateEulerM(mRotationMatrix, 0, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360);
Matrix.multiplyMV(mResultVector, 0, mRotationMatrix, 0, mDirectionVector, 0);
particleSystem.addParticle(
position,
color,
new Geometry.Vector(mResultVector[0],
mResultVector[1],
mResultVector[2]),
curTime);
}
}
2.2 然后在Render中使用ParticleFireworksExplosion作為粒子發(fā)射器
public class ParticlesRender implements GLSurfaceView.Renderer {
...
private ParticleFireworksExplosion particleFireworksExplosion;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
...
particleFireworksExplosion = new ParticleFireworksExplosion();
}
@Override
public void onDrawFrame(GL10 gl) {
...
//粒發(fā)射器添加粒子
// mParticleShooter.addParticles(mParticleSystem,curTime,20);
int color = Color.rgb(255, 50, 5);
//煙花爆炸粒子發(fā)生器 添加粒子
particleFireworksExplosion.addExplosion(
mParticleSystem,
new Geometry.Point(
0,
0f ,
0),
color,
curTime);
...
}
效果如下:
問題1: 這明顯不是煙花爆炸的效果
煙花爆炸是一個時間段只有一個爆炸,然后煙花顆粒向外圓形擴展開际度。而目前實現(xiàn)的效果是不斷的發(fā)射新的粒子葵袭。
為此需要在onDrawFrame中間隔一段時間才發(fā)射粒子,這樣讓“煙花飛一會”從而形成煙花爆炸效果乖菱;同時坡锡,為了同一時間段粒子的數(shù)量保持一致蓬网,我們多同一個時間點我們同發(fā)射寫粒子。具體實現(xiàn)如下:
public class ParticleFireworksExplosion {
...
private final int mPreAddParticleCount = 100;
public void addExplosion(ParticleSystem particleSystem, Geometry.Point position, int color, float curTime) {
//不是OnDrawFrame就添加煙花爆炸粒子鹉勒,而是采用1/100的采樣率 帆锋,讓粒子飛一會,從而產(chǎn)生煙花爆炸效果
if (mRandom.nextFloat() < 1.0f / mPreAddParticleCount) {
//同一時刻添加100個方向360隨機的粒子
for (int i = 0; i < mPreAddParticleCount; i++) {
Matrix.setRotateEulerM(mRotationMatrix, 0, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360);
Matrix.multiplyMV(mResultVector, 0, mRotationMatrix, 0, mDirectionVector, 0);
particleSystem.addParticle(
position,
color,
new Geometry.Vector(mResultVector[0],
mResultVector[1],
mResultVector[2]),
curTime);
}
}
}
}
效果如下:
問題2: 煙花擴散的效果不是圓形而是橢圓
這需要采用投影矩陣和視圖矩陣變換 把橢圓形狀投影到屏幕贸弥。
為此我們需要做如此處理
1. 頂點著色器中添加unifrom mat4窟坐,在gl_Position的賦值需要和改矩陣相乘。
2. Program中解析該location以及進行數(shù)據(jù)的輸入
3. Render中進行透視矩陣和試圖矩陣的定義和計算绵疲,生成matrix數(shù)據(jù)
具體實現(xiàn)如下:
1. 頂點著色器
uniform float u_Time;
uniform mat4 u_Matrix; //定義矩陣數(shù)據(jù)類型變量
attribute vec3 a_Position;
attribute vec3 a_Color;
attribute vec3 a_Direction;
attribute float a_PatricleStartTime;
varying vec3 v_Color;
varying float v_ElapsedTime;
void main(){
v_Color = a_Color;
//粒子已經(jīng)持續(xù)時間 當(dāng)前時間-開始時間
v_ElapsedTime = u_Time - a_PatricleStartTime;
//重力或者阻力因子哲鸳,隨著時間的推移越來越大
float gravityFactor = v_ElapsedTime * v_ElapsedTime / 9.8;
//當(dāng)前的運動到的位置 粒子起始位置+(運動矢量*持續(xù)時間)
vec3 curPossition = a_Position + (a_Direction * v_ElapsedTime);
//減去重力或阻力的影響
curPossition.y -= gravityFactor;
//把當(dāng)前位置通過內(nèi)置變量傳給片元著色器。
// gl_Position = vec4(curPossition,1.0); //注釋掉該實現(xiàn)盔憨,修改為下面的實現(xiàn)徙菠。
//把當(dāng)前位置和MVP矩陣相乘后,通過內(nèi)置變量傳給片元著色器
gl_Position = u_Matrix * vec4(curPossition, 1.0);
gl_PointSize = 25.0;
}
2. Program中解析和賦值matrix
public class ParticleShaderProgram {
...
private final String U_MATRIX = "u_Matrix";
private final int uMatrixLocation;
public ParticleShaderProgram(Context context) {
...
//獲取uniform 和attribute的location
uMatrixLocation = glGetUniformLocation(program, U_MATRIX);
...
}
/**
* 設(shè)置Uniform變量
* @param matrix
* @param curTime
*/
public void setUniforms(float[] matrix, float curTime, int textureId){
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
...
}
}
3. Render中進行透視矩陣和試圖矩陣的定義和計算郁岩,生成matrix數(shù)據(jù)
public class ParticlesRender implements GLSurfaceView.Renderer {
...
private final float[] projectionMatrix = new float[16];
private final float[] viewMatrix = new float[16];
private final float[] viewProjectionMatrix = new float[16];
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0,0,width,height);
Matrix.perspectiveM(projectionMatrix, 0,45, (float) width
/ (float) height, 1f, 10f);
setIdentityM(viewMatrix, 0);
translateM(viewMatrix, 0, 0f, -1.5f, -5f);
multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0,
viewMatrix, 0);
}
@Override
public void onDrawFrame(GL10 gl) {
...
//設(shè)置Uniform變量
mProgram.setUniforms(viewProjectionMatrix,curTime,mTextureId);
}
}
效果如下:
問題3婿奔、目前的煙花都是紅色的,如何實現(xiàn)多彩的煙花问慎?
只需要修改粒子的顏色即可萍摊,我們通過hsv顏色空間,隨機調(diào)節(jié)色調(diào)如叼,然后轉(zhuǎn)為rgb顏色空間的顏色值冰木,賦值給粒子,具體實現(xiàn)如下
public class ParticleFireworksExplosion {
private float[] mDirectionVector = {0f, 0f, 0.3f, 1f};
private float[] mResultVector = new float[4];
private final Random mRandom = new Random();
private float[] mRotationMatrix = new float[16];
private final int mPreAddParticleCount = 100;
// 定義hsv色彩空間值笼恰,色調(diào)值默認(rèn)為0(對應(yīng)角度范圍為0-360)踊沸,飽和度和亮度默認(rèn)為1(范圍為0-2)
private final float[] hsv = {0f, 1f, 1f};
public void addExplosion(ParticleSystem particleSystem, Geometry.Point position, float curTime) {
//不是OnDrawFrame就添加煙花爆炸粒子,而是采用1/100的采樣率 社证,讓粒子飛一會逼龟,從而產(chǎn)生煙花爆炸效果
if (mRandom.nextFloat() < 1.0f / mPreAddParticleCount) {
//隨機生成顏色的色調(diào),
hsv[0] = random.nextInt(360);
//把hsv顏色空間轉(zhuǎn)位rgb顏色空間值
int color = Color.HSVToColor(hsv);
//同一時刻添加100*3個方向360隨機的粒子
for (int i = 0; i < mPreAddParticleCount *3 ; i++) {
Matrix.setRotateEulerM(mRotationMatrix, 0, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360, mRandom.nextFloat() * 360);
Matrix.multiplyMV(mResultVector, 0, mRotationMatrix, 0, mDirectionVector, 0);
particleSystem.addParticle(
position,
color,
new Geometry.Vector(mResultVector[0],
mResultVector[1]+0.3f,//由于煙花彈向上的慣性追葡,爆炸時添加向上的偏移腺律,效果看起來更加逼真。
mResultVector[2]),
curTime);
}
}
}
}
效果正如開篇展示
參考
《OpenGL ES 3.0 編程指南》
《OpenGL ES應(yīng)用開發(fā)實踐指南》
[粒子系統(tǒng)--煙花 [OpenGL-Transformfeedback]]
[Android制作粒子爆炸特效]
[Android超強大的粒子動畫庫辽俗,流星雨疾渣,爆炸,滿屏飛花崖飘,等粒子特效快速實現(xiàn)]
[OpenGL進階(六)-粒子系統(tǒng)]
[【OpenGL】Shader實例分析(七)- 雪花飄落效果]
收獲
通過對OpenGL ES粒子系統(tǒng)的學(xué)習(xí)實踐,發(fā)現(xiàn)通過粒子可以制作很多絢麗的效果杈女。也在學(xué)習(xí)實踐的過程中越來越發(fā)現(xiàn)路還很長朱浴,要不斷持續(xù)學(xué)習(xí)實踐才行吊圾。
具體到本篇收獲
分析煙花爆炸的場景與特性
通過實踐逐步實現(xiàn)多彩的煙花效果
遇到的問題解決
感謝你的閱讀
本來這個OpenGL ES系列計劃還要寫2-3篇,把天空盒翰蠢、光照等也逐步學(xué)習(xí)實踐项乒,但是感覺到應(yīng)用場景和整體方向目前來看關(guān)系性還不是很大。所以準(zhǔn)備放大后續(xù)環(huán)節(jié)學(xué)習(xí)實踐梁沧。下一篇我們開啟JNI和NDK系列的學(xué)習(xí)實踐檀何。歡迎關(guān)注公眾號“音視頻開發(fā)之旅”,一起學(xué)習(xí)成長廷支。
歡迎交流