Opengl ES系列学习--用粒子增添趣味

     我们本节开始分析《OpenGL ES应用开发实践指南 Android卷》书中第10章中的粒子系统的实现原理,搞清楚其中的代码逻辑,代码下载请点击:Opengl ES Source Code,该Git库中的particles Module就是我们本节要分析的目标,先看下本节最终实现的结果。

粒子系统

     最终运行在真机上的效果非常炫,三个红绿蓝粒子系统不断的发射新的粒子,所有粒子由于重力上升到一定高度后开始下降,而且颜色发亮,非常漂亮,下面让我们看一下它到底是怎么实现的。

     首先,我们来看一下ParticlesActivity类中的代码,非常简单,判断当前设备是否支持Opengl ES2.0,如果支持,则调用glSurfaceView.setEGLContextClientVersion(2)将版本设置为2.0,然后构造一个Render渲染类,调用setRenderer设置为glSurfaceView的渲染器。

     接下来看一下ParticlesRenderer类,首先必须实现android.opengl.GLSurfaceView.Renderer,重写父类定义的onSurfaceCreated、onSurfaceChanged、onDrawFrame三个方法,然后在每个方法中添加实现逻辑,三个方法的回调意图也非常清晰,onSurfaceCreated就是当GLSurfaceView创建完成后的回调,到这里显示系统分配给当前View的Surface才有效,才可以执行绘图工作;onSurfaceChanged表示Surface发生变化时的回调,最明显的就是当前Activity退出再进入,Surface可见性变化,就会回调该方法;onDrawFrame表示需要绘制一帧,这里就和Vsync垂直同步信号相关了,显示器一般是60FPS帧率,大家可以看下我之前Vsync相关的博客。

    private final Context context;

    private final float[] projectionMatrix = new float[16];    
    private final float[] viewMatrix = new float[16];
    private final float[] viewProjectionMatrix = new float[16];
    /*
    // Maximum saturation and value.
    private final float[] hsv = {0f, 1f, 1f};*/
    
    private ParticleShaderProgram particleProgram;      
    private ParticleSystem particleSystem;
    private ParticleShooter redParticleShooter;
    private ParticleShooter greenParticleShooter;
    private ParticleShooter blueParticleShooter;
    /*private ParticleFireworksExplosion particleFireworksExplosion;
    private Random random;*/
    private long globalStartTime;
    private int texture;

     以上是ParticlesRenderer类中定义的所有成员变量,首先是三个float数组,长度全部为16,这三个数组是进行Matrix矩阵运算需要的,首先调用MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f)获取到一个透视投影矩阵,45表示视角为45度,(float) width / (float) height表示缩放率,这里请一定注意,不能直接用width和height相除,因为我们如果是竖屏的话,宽度比高度小,结果得到的是0,不会有任何绘制,1和10表示z轴的可视范围从-1到-10,大家看下MatrixHelper中该方法的实现就会明白了;其次调用setIdentityM(viewMatrix, 0)得到一个4*4单位矩阵,如果大家不明白单位矩阵的话,请百度搜索搞明白。为什么是4*4呢?因为矩阵中一般运算都是四个分量,比如顶点位置属性(x、y、z、w),w分量其实非常有用,书中有很详细的解释;接着调用translateM(viewMatrix, 0, 0f, -1.5f, -5f)将viewMatrix单位矩阵进行平移,-1.5f表示Y方向1.5个单位,负值也就是向下,-5f表示Z方向5个单位,负值表示离视点(屏幕)越远,这里需要注意,因为我们创建透视投影矩阵时,指定的Z轴最近距离是一个单位,最远距离是10个单位,只有在这个范围内的顶点才会被绘制,如果Z轴的值超出这个范围,也不会有任何东西被绘制,大家可以试一下;最后调用multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0)将两个矩阵相乘,第一个参数viewProjectionMatrix是存储输出结果的,看到这里,大家就明白这三个float数组的作用了吧,其实就是得到两个矩阵,然后执行乘法运算,把结果存储在第三个矩阵中。

     接下来是ParticleShaderProgram particleProgram是自定义的一个着色器程序,ParticleSystem particleSystem是自定义的粒子系统,redParticleShooter、greenParticleShooter、blueParticleShooter是三个粒子发射器,等下分析完当前类,我们逐个分析这三个自定义的类。globalStartTime记录一个时间戳,texture是纹理ID。

     构造方法很简单,我们就不说了,继续看onSurfaceCreated,代码如下:

    @Override
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        
        // Enable additive blending
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        
        particleProgram = new ParticleShaderProgram(context);        
        particleSystem = new ParticleSystem(10000);        
        globalStartTime = System.nanoTime();
        
        final Vector particleDirection = new Vector(0f, 0.5f, 0f);
        
        final float angleVarianceInDegrees = 5f; 
        final float speedVariance = 1f;
        
        /*
        redParticleShooter = new ParticleShooter(
            new Point(-1f, 0f, 0f), 
            particleDirection,                
            Color.rgb(255, 50, 5));
        
        greenParticleShooter = new ParticleShooter(
            new Point(0f, 0f, 0f), 
            particleDirection,
            Color.rgb(25, 255, 25));
        
        blueParticleShooter = new ParticleShooter(
            new Point(1f, 0f, 0f), 
            particleDirection,
            Color.rgb(5, 50, 255));     
        */
        redParticleShooter = new ParticleShooter(
            new Point(-1f, 0f, 0f), 
            particleDirection,                
            Color.rgb(255, 50, 5),            
            angleVarianceInDegrees, 
            speedVariance);
        
        greenParticleShooter = new ParticleShooter(
            new Point(0f, 0f, 0f), 
            particleDirection,
            Color.rgb(25, 255, 25),            
            angleVarianceInDegrees, 
            speedVariance);
        
        blueParticleShooter = new ParticleShooter(
            new Point(1f, 0f, 0f), 
            particleDirection,
            Color.rgb(5, 50, 255),            
            angleVarianceInDegrees, 
            speedVariance); 
        /*
        particleFireworksExplosion = new ParticleFireworksExplosion();
        
        random = new Random();  */
        
        texture = TextureHelper.loadTexture(context, R.drawable.particle_texture);
    }

     glClearColor的作用就是清屏,传入的参数就是RGBA分量,四个0表示什么也没有,就是黑色,如果要用白色清屏,那RGB肯定都要设置成1了;glEnable(GL_BLEND)表示启用Blend混合,Blend混合是将源色和目标色以某种方式混合生成特效的技术。混合常用来绘制透明或半透明的物体。在混合中起关键作用的α值实际上是将源色和目标色按给定比率进行混合,以达到不同程度的透明。α值为0则完全透明,α值为1则完全不透明。混合操作只能在RGBA模式下进行,颜色索引模式下无法指定α值。物体的绘制顺序会影响到OpenGL的混合处理,说的通俗点,比如我们下落的粒子和刚生成的粒子重叠,两个粒子的颜色就会混合在一起,大家可以关闭混合就会看到明显的效果;glBlendFunc(GL_ONE, GL_ONE)就是叠加混合,Opengl还提供了其他很多的混合算法,大家可以自己去研究。接着创建着色器程序和粒子系统,粒子系统的参数10000表示最大粒子容量为10000,如果超出的话,就会从头存储,后面看到这里的代码就会明白,然后给globalStartTime赋值,也就是记录当前时间,接下来创建Vector向量,X和Z轴的值都为0,只有Y轴为正,该值会影响到粒子往上飞。然后定义角度为5度,速度为1,接着构造三个粒子发射器,第一个参数表示粒子发射器的位置,当前坐标系统,(0,0,0)为屏幕正中心,X轴正向向右,Y轴正向向上,Z轴正向向外(朝着我们的眼睛),所以三个粒子发射器的定位就是左中(-1,0,0)、中心(0,0,0)、右中(1,0,0),大家看下实际的效果粒子的Y轴位置不在中间,这是因为使用透视投影矩阵和平移的结果导致的,第二个参数方向向量都一样,然后是RGB颜色,接着是角度和速度;该方法最后一句texture = TextureHelper.loadTexture(context, R.drawable.particle_texture)表示加载纹理,我们跟进去看一下该方法的实现,源码如下:

public static int loadTexture(Context context, int resourceId) {
        final int[] textureObjectIds = new int[1];
        glGenTextures(1, textureObjectIds, 0);

        if (textureObjectIds[0] == 0) {
            if (LoggerConfig.ON) {
                Log.w(TAG, "Could not generate a new OpenGL texture object.");
            }

            return 0;
        }
        
        final BitmapFactory.Options op
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红-旺永福

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值