1.粒子特效

粒子特效:Particles Effect,粒子特效本质上是通过一次或者多次渲染绘制出大量位置、形状或者颜色不同的物体(粒子),形成大量粒子运动的视觉效果。所以,粒子特效天然适合用OpenGL ES 实例化(Instancing)实现。

2.定义粒子

定义粒子,通常一个粒子有一个生命值,生命值结束该粒子消失,还有描述粒子在(x, y, z)三个方向的位置(偏移)和运动速度,以及粒子的颜色等属性。可以把粒子定义成一个结构体:

struct Particle {GLfloat dxSpeed,dySpeed,dzSpeed;// 粒子速度GLubyte r,g,b,a; //r,g,b,a      //粒子颜色GLfloat dx,dy,dz; // 粒子的位置GLfloat life;               //粒子出现的时长GLfloat cameraDistance;Particle(){dxSpeed = 1.0f;dySpeed = 1.0f;dzSpeed = 1.0f;dx = 0.0f;dy = 0.0f;dz = 0.0f;r = static_cast<GLubyte>(1.0f);g = static_cast<GLubyte>(1.0f);b = static_cast<GLubyte>(1.0f);a = static_cast<GLubyte>(1.0f);life = 10.0f;  //粒子的生命值}/*操作符重载 < */bool operator<(const Particle& that) const {return this->cameraDistance > that.cameraDistance;}
};

3.顶点着色器

#version 300 es
precision mediump float;
layout(location = 0) in vec3 a_vertex;          //粒子顶点
layout(location = 1) in vec2 a_texCoord;        //粒子材质
layout(location = 2) in vec3 a_offset;          //粒子位置
layout(location = 3) in vec4 a_particlesColor;    //粒子颜色uniform mat4 u_mat;    //MVP矩阵   观察矩阵*模型矩阵*透视矩阵/*输出的材质和颜色*/
out vec2 v_texCoord;
out vec4 v_color;void main() {gl_Position = u_mat * vec4(a_vertex - vec3(0.0, 0.95, 0.0) + a_offset, 1.0);v_texCoord = a_texCoord;v_color = a_particlesColor;
}

属性a_offset是粒子的位置偏移,最终确定粒子的位置,属性a_particlesColor表示照在粒子表面光的颜色,这两个属性均为实例化数组,因为每个粒子有不同的位置和颜色。

将粒子相关的属性传进来,mvp矩阵传进来,对位置做简单的计算,并将颜色和材质传递给片元着色器

4.片元着色器

#version 300 es
precision mediump float;
in vec2 v_texCoord;
in vec4 v_color;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;void main() {outColor = texture(s_TextureMap, v_texCoord) * v_color;
//    outColor = texture(s_TextureMap, v_texCoord) ;
}

如果粒子希望显示材质本身,可以直接将采样器得到的颜色赋值给outColor,不用再跟v_color相乘

5.程序实现过程代码解析

5.1绘制前的准备
glGenBuffers(1, &m_ParticlesVertexVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),g_vertex_buffer_data,GL_STATIC_DRAW);glGenBuffers(1, &m_ParticlesPosVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat),NULL, GL_DYNAMIC_DRAW);glGenBuffers(1, &m_ParticlesColorVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte),NULL, GL_DYNAMIC_DRAW);

创建VBO,注意:粒子的顶点是GL_STATIC_DRAW,位置和颜色会动态变化,所以是GL_DYNAMIC_DRAW

// Generate VAO Id
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);/*给shader传递数据*/
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5 * sizeof(GLfloat),(void *) 0
);glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,5 * sizeof(GLfloat),(const void *) (3 * sizeof(GLfloat))
);glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);
glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,0,(void *) 0
);glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
glVertexAttribPointer(3,4,GL_UNSIGNED_BYTE,GL_TRUE,0,(void *) 0
);
  • 创建并绑定VAO

  • 给shader中的变量a_vertex、a_texCoord、a_offset、a_particlesColor赋值

glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 0);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);

glVertexAttribDivisor(0, 0);表示实例化绘制时对 index =0 的属性不更新;glVertexAttribDivisor(2, 1); 用于指定 index = 2 的属性为实例化数组,1 表示每绘制一个实例,更新一次数组中的元素。

5.2 进行绘制操作
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glDisable(GL_BLEND); //关闭混合//    value += 1;
//    value = value % 360;
//    value = value % 360;//转化为弧度角float radiansX = static_cast<float>(MATH_PI / 180.0f * value);glm::mat4 modelMat = glm::mat4(1.0f);  //模型矩阵modelMat = glm::scale(modelMat, glm::vec3(0.8f, 0.8f, 0.8f));modelMat = glm::rotate(modelMat, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));modelMat = glm::rotate(modelMat, radiansX, glm::vec3(0.0f, 1.0f, 0.0f));modelMat = glm::translate(modelMat, glm::vec3(0.0f, 0.0f, 0.0f));glm::mat4 projection =glm::perspective(45.0f, 0.5f, 0.1f, 100.f);// viewMat matrixglm::mat4 viewMat = glm::lookAt(glm::vec3(0, 6, 0), // Camera is at (0,0,1), in World Spaceglm::vec3(0, 0, 0), // and looks at the originglm::vec3(0, 0, 1)  // Head is up (set to 0,-1,0 to look upside-down));glm::mat4 mvpMatrix = projection * viewMat * modelMat;
  • 清理了深度和颜色缓冲
  • 创建模型矩阵
  • 创建透视矩阵
  • 创建观察者矩阵
  • 生成MVP矩阵
int particleCount = UpdateParticles();m_pOpenGLShader->Bind();
glBindVertexArray(m_VaoId);m_pOpenGLShader->SetUniformValue("u_mat",mvpMatrix);// Bind the RGBA map
m_pOpenGLShader->SetUniformValue("s_TextureMap",0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texID[3]);glDrawArraysInstanced(GL_TRIANGLES, 0, 36, particleCount);
  • 更新粒子信息
  • 把MVP矩阵传给Shader
  • 初始化采样器
  • 实例化进行绘制
void MSParticlesSample::GenerateNewParticle(Particle &particle) {particle.life = 10.0f;particle.cameraDistance = -1.0f;particle.dx = (rand() % 2000 - 1000.0f) / 3000.0f;particle.dy = (rand() % 2000 - 1000.0f) / 3000.0f;particle.dz = (rand() % 2000 - 1000.0f) / 3000.0f;float spread = 1.5f;glm::vec3 maindir = glm::vec3(0.0f, 2.0f, 0.0f);glm::vec3 randomdir = glm::vec3((rand() % 2000 - 1000.0f) / 1000.0f,(rand() % 2000 - 1000.0f) / 1000.0f,(rand() % 2000 - 1000.0f) / 1000.0f);glm::vec3 speed = maindir + randomdir * spread;particle.dxSpeed = speed.x;particle.dySpeed = speed.y;particle.dzSpeed = speed.z;particle.r = static_cast<unsigned char>(rand() % 256);particle.g = static_cast<unsigned char>(rand() % 256);particle.b = static_cast<unsigned char>(rand() % 256);particle.a = static_cast<unsigned char>((rand() % 256) / 3);}

新粒子的速度、偏移以及颜色都是随机生成的

/*** 查找生命值结束的粒子* @return */
int MSParticlesSample::FindUnusedParticle() {for (int i = m_LastUsedParticle; i < MAX_PARTICLES; i++){if (m_ParticlesContainer[i].life <= 0){m_LastUsedParticle = i;return i;}}for (int i = 0; i < m_LastUsedParticle; i++){if (m_ParticlesContainer[i].life <= 0){m_LastUsedParticle = i;return i;}}return -1;
}

查找生命值结束的粒子

int MSParticlesSample::UpdateParticles() {int newParticles = 300;for (int i = 0; i < newParticles; i++){int particleIndex = FindUnusedParticle();if (particleIndex >= 0){GenerateNewParticle(m_ParticlesContainer[particleIndex]);}}int particlesCount = 0;for (int i = 0; i < MAX_PARTICLES; i++){Particle &p = m_ParticlesContainer[i]; // shortcutif (p.life > 0.0f){float delta = 0.1f;glm::vec3 speed = glm::vec3(p.dxSpeed, p.dySpeed, p.dzSpeed), pos = glm::vec3(p.dx,p.dy,p.dz);p.life -= delta;if (p.life > 0.0f){//模拟简单的物理:只有重力,没有碰撞speed += glm::vec3(0.0f, 0.81f, 0.0f) * delta * 0.5f;pos += speed * delta;p.dxSpeed = speed.x;p.dySpeed = speed.y;p.dzSpeed = speed.z;p.dx = pos.x;p.dy = pos.y;p.dz = pos.z;m_pParticlesPosData[3 * particlesCount + 0] = p.dx;m_pParticlesPosData[3 * particlesCount + 1] = p.dy;m_pParticlesPosData[3 * particlesCount + 2] = p.dz;m_pParticlesColorData[4 * particlesCount + 0] = p.r;m_pParticlesColorData[4 * particlesCount + 1] = p.g;m_pParticlesColorData[4 * particlesCount + 2] = p.b;m_pParticlesColorData[4 * particlesCount + 3] = p.a;}particlesCount++;}}glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat), NULL,GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, particlesCount * sizeof(GLfloat) * 3, m_pParticlesPosData);glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte), NULL,GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, particlesCount * sizeof(GLubyte) * 4,m_pParticlesColorData);return particlesCount;}

更新粒子(更新粒子的位置、运动速度和生命值),然后更新实例化数组

Android OpenGL ES 3.0 粒子特效相关推荐

  1. Android OpenGL ES 2.0 屏幕坐标和3D世界坐标转换

    Android OpenGL ES 2.0 屏幕坐标和3D世界坐标转换 查看全文 http://www.taodudu.cc/news/show-6705596.html 相关文章: word中如何加 ...

  2. 使用Android OpenGL ES 2.0绘图之五:添加运动

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  3. 使用Android OpenGL ES 2.0绘图之一:搭建一个OpenGL ES环境

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  4. Android opengl es 3.0 + ndk 绘画涂鸦项目

    前言 写一个opengl es 3.0 + ndk 的绘画涂鸦项目,命名为白板哈哈哈,记录自己遇到的问题,顺便学到的知识整合一遍,算是对自己一段时间的总结. 项目地址:Whiteboard 如果对你有 ...

  5. android opengl es 纹理 不同设备 白色,android – OpenGL ES 2.0纹理没有在某些设备上显示...

    早上好,这是2个纹理非幂的典型例子. 由于多种原因,纹理在分辨率上需要2的幂,这是一个非常常见的错误,每个人都碰巧陷入这个陷阱:)我也是. 2个纹理的非功率在某些设备/ GPU上运行平稳的事实,仅仅取 ...

  6. 使用Android OpenGL ES 2.0绘图之三:绘制形状

    传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 在定义好待绘制的形状之后,就要开始绘制它们了.使用OpenGL ES 2.0绘制形状可 ...

  7. 使用Android OpenGL ES 2.0绘图之四:应用投影和相机视图

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  8. 使用Android OpenGL ES 2.0绘图之二:定义形状

    传送门☞Android兵器谱☞转载请注明☞http://blog.csdn.net/leverage_1229 传送门☞系统架构设计☞转载请注明☞http://blog.csdn.net/levera ...

  9. android opengl es 2.0 编程指南,Android OpenGL ES 2.0 初次体验

    本文目录 一. OpenGL ES是什么? 二. OpenGL ES的版本 三. EGL是什么? 四. 需要知道的两个方法 五. 在Android中使用OpenGL ES的步骤 六. 例子1:简单的程 ...

最新文章

  1. shell监控java接口服务_Linux系统下Java通过shell脚本监控重启服务
  2. python如何实现多进程_Python实现多进程的四种方式
  3. SpringBoot_异常_01_Caused by: java.lang.BootstrapMethodError: java.lang.NoSuchMethodError
  4. QT+OPENCV实现录屏功能
  5. 我用Python爬虫挣钱的那些事
  6. 池州天气预报软件测试,池州天气预报15天
  7. [html]html实现页面跳转都有哪些方法?
  8. 安装redis提示[test] error 2_技术干货分享:一次flask+redis的微服务实战
  9. 微软拥抱开源_如何拥抱开源劳动力
  10. Android Activity类详解
  11. python runner_【httprunner】自动化测试入门(基于python)!
  12. 马里兰大学calce电池循环测试数据集_锂电池极片:机械性能测试是门学问,要搞懂真不容易...
  13. 计算机宏应用实例,【电脑技巧】Office word宏命令巧妙应用一例
  14. 用Excel做直方图(2):频率分布直方图
  15. 基于对话框的MFC程序加载位图为背景图案
  16. LeetCode算题准备内容
  17. STM32 keil如何下载STM32芯片F1/F4固件库 .pack文件
  18. 最新正版防破解网络还原精灵免费推出
  19. 仓储+调度,YOGO智能配送站能否改变外卖配送格局?...
  20. UltraCompare 22:文本对比工具 Mac版

热门文章

  1. 年度催泪之作:2015中国程序员生存报告
  2. Linux-京东字节百度提前批,一面二面都被问到了awk——实例篇(4)ip地址相关
  3. 2017AI最成功案例
  4. 怎么连接云服务器共享文件夹,如何设置局域网共享文件夹
  5. 简单的小程序——可操作的表白神器
  6. 支持十亿级密态数据、低代码,蚂蚁集团发布隐语开放平台
  7. A - Artwork Gym - 101550A
  8. 洞察市场需求,深耕大健康赛道,缤跃酒店打造一站式运动酒店品牌
  9. PO、VO、DO、TO、DTO、 BO、 QO、DAO、POJO
  10. 66.假定输入的字符串中只包含字母和*号。请编写函数fun,它的功能是:删除字符串中所有的*号。在编写函数时,不得使用C语言提供的字符串函数。