音视频开发之旅(16) OpenGL ES粒子效果-烟花爆炸

目录

  1. 烟花爆竹场景和属性
  2. 实践以及遇到的问题
  3. 资料
  4. 收获

通过该篇的实践实现如下效果

 

一、烟花爆竹场景和属性

在上一篇 音视频开发之旅(15) OpenGL ES粒子系统 - 喷泉 的基础上 实现烟花爆炸效果。

在具体实践之前,先来想一想,烟花爆炸的场景和属性
场景:从中心点开始爆炸,然后烟花粒子向各个方向炸开,整体形状也各有不同,比如 北京奥运会的大脚印,但是大部分还是圆形(因为设计实现相对简单),今天我们也实现一个圆形的烟花爆炸效果。
属性:颜色、角度、运动矢量、形状。

实现流程和上一篇基本一致,不清晰整理的流程,建议先回看下

二、实践:烟花效果

在上篇的基础上通过修改Render的onDrawFrame中的粒子发射器来逐步实现烟花爆炸效果。

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作为粒子发射器

 

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) {
     ...

        //粒发射器添加粒子
//        mParticleShooter.addParticles(mParticleSystem,curTime,20);
                        int color = Color.rgb(255, 50, 5);

        //烟花爆炸粒子发生器 添加粒子
        particleFireworksExplosion.addExplosion(
                mParticleSystem,
                new Geometry.Point(
                        0,
                        0f ,
                        0),
                color,
                curTime);
        ...

    }

效果如下:

 

问题1: 这明显不是烟花爆炸的效果

烟花爆炸是一个时间段只有一个爆炸,然后烟花颗粒向外圆形扩展开。而目前实现的效果是不断的发射新的粒子。

为此需要在onDrawFrame中间隔一段时间才发射粒子,这样让“烟花飞一会”从而形成烟花爆炸效果;同时,为了同一时间段粒子的数量保持一致,我们多同一个时间点我们同发射写粒子。具体实现如下:

 

public class ParticleFireworksExplosion {

    ...

    private final int mPreAddParticleCount = 100;

    public void addExplosion(ParticleSystem particleSystem, Geometry.Point position, int color, float curTime) {

        //不是OnDrawFrame就添加烟花爆炸粒子,而是采用1/100的采样率 ,让粒子飞一会,从而产生烟花爆炸效果
        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以及进行数据的输入
3.  Render中进行透视矩阵和试图矩阵的定义和计算,生成matrix数据

具体实现如下:

1. 顶点着色器

 

uniform float u_Time;
uniform mat4 u_Matrix; //定义矩阵数据类型变量

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;
    //粒子已经持续时间  当前时间-开始时间
    v_ElapsedTime = u_Time - a_PatricleStartTime;
    //重力或者阻力因子,随着时间的推移越来越大
    float gravityFactor = v_ElapsedTime * v_ElapsedTime / 9.8;
    //当前的运动到的位置 粒子起始位置+(运动矢量*持续时间)
    vec3 curPossition = a_Position + (a_Direction * v_ElapsedTime);
    //减去重力或阻力的影响
    curPossition.y -= gravityFactor;

    //把当前位置通过内置变量传给片元着色器。
//    gl_Position =  vec4(curPossition,1.0); //注释掉该实现,修改为下面的实现。

    //把当前位置和MVP矩阵相乘后,通过内置变量传给片元着色器
    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);

        ...
    }

    /**
     * 设置Uniform变量
     * @param matrix
     * @param curTime
     */
    public void setUniforms(float[] matrix, float curTime, int textureId){
        GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);

       ...

    }
}

3. Render中进行透视矩阵和试图矩阵的定义和计算,生成matrix数据

 

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) {
      ...
        //设置Uniform变量 
        mProgram.setUniforms(viewProjectionMatrix,curTime,mTextureId);
    }
}

效果如下:

 

问题3、目前的烟花都是红色的,如何实现多彩的烟花?

只需要修改粒子的颜色即可,我们通过hsv颜色空间,随机调节色调,然后转为rgb颜色空间的颜色值,赋值给粒子,具体实现如下

 

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色彩空间值,色调值默认为0(对应角度范围为0-360),饱和度和亮度默认为1(范围为0-2)
    private final float[] hsv = {0f, 1f, 1f};


    public void addExplosion(ParticleSystem particleSystem, Geometry.Point position, float curTime) {


        //不是OnDrawFrame就添加烟花爆炸粒子,而是采用1/100的采样率 ,让粒子飞一会,从而产生烟花爆炸效果
        if (mRandom.nextFloat() < 1.0f / mPreAddParticleCount) {

        //随机生成颜色的色调,
        hsv[0] = random.nextInt(360);
        //把hsv颜色空间转位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);
            }
        }
    }
}

效果正如开篇展示

 

完整代码已上传至github

资料

《OpenGL ES 3.0 编程指南》
《OpenGL ES应用开发实践指南》

[粒子系统--烟花 [OpenGL-Transformfeedback]]
[Android制作粒子爆炸特效]
[Android超强大的粒子动画库,流星雨,爆炸,满屏飞花,等粒子特效快速实现]
[OpenGL进阶(六)-粒子系统]
[【OpenGL】Shader实例分析(七)- 雪花飘落效果]

收获

通过对OpenGL ES粒子系统的学习实践,发现通过粒子可以制作很多绚丽的效果。也在学习实践的过程中越来越发现路还很长,要不断持续学习实践才行。

具体到本篇收获
1. 分析烟花爆炸的场景与特性

2. 通过实践逐步实现多彩的烟花效果
3. 遇到的问题解决

感谢你的阅读

本来这个OpenGL ES系列计划还要写2-3篇,把天空盒、光照等也逐步学习实践,但是感觉到应用场景和整体方向目前来看关系性还不是很大。所以准备放大后续环节学习实践。下一篇我们开启JNI和NDK系列的学习实践。欢迎关注公众号“音视频开发之旅”,一起学习成长。

欢迎交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值