OpenGL ES 实例化(Instancing)是一种只调用一次渲染函数就能绘制出很多物体的技术,可以实现将数据一次性发送给 GPU ,告诉 OpenGL ES 使用一个绘制函数,将这些数据绘制成多个物体。
实例化(Instancing)避免了 CPU 多次向 GPU 下达渲染命令(避免多次调用 glDrawArrays 或 glDrawElements 等绘制函数),节省了绘制多个物体时 CPU 与 GPU 之间的通信时间,提升了渲染性能。
对于使用,实例化调用和普通调用
相对于普通绘制,实例化绘制多了一个参数 instancecount,表示需要渲染的实例数量,调用完实例化绘制函数后,我们便将绘制数据一次性发送给 GPU,然后告诉它该如何使用一个函数来绘制这些实例。
//普通渲染
glDrawArrays (GLenum mode, GLint first, GLsizei count);
glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
//实例化渲染
glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);
着色器中多出gl_InstanceID
实例化(Instancing)的目标并不是实现将同一物体绘制多次,而是能基于某一物体绘制出位置、大小、形状或者颜色不同的多个物体。OpenGL ES 着色器中有一个与实例化绘制相关的内建变量 gl_InstanceID。
gl_InstanceID 表示当前正在绘制实例的 ID ,每个实例对应一个唯一的 ID ,通过这个 ID 可以轻易实现基于一个物体而绘制出位置、大小、形状或者颜色不同的多个物体(实例)
利用内建变量 gl_InstanceID 在 3D 空间绘制多个位于不同位置的立方体,利用 u_offsets[gl_InstanceID] 对当前实例的位置进行偏移,对应的着色器脚本:
// vertex shader GLSL
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
uniform mat4 u_MVPMatrix;
uniform vec3 u_offsets[125];
void main()
{
//通过 u_offsets[gl_InstanceID] 对当前实例的位置进行偏移
gl_Position = u_MVPMatrix * (a_position + vec4(u_offsets[gl_InstanceID], 1.0));
v_texCoord = a_texCoord;
}
因为利用内建变量 gl_InstanceID 和偏移数组进行实例化绘制还存在一个问题,那就是着色器中 uniform 类型数据存在上限,也就是 u_offsets 这个数组的大小有限制,最终导致我们绘制的实例存在上限。
为了避免这个问题,我们可以使用实例化数组(Instanced Array),它使用顶点属性来定义,这样就允许我们使用更多的数据,而且仅当顶点着色器渲染一个新实例时它才会被更新。
引入glVertexAttribDivisor,它表示 OpenGL ES 什么时候去更新顶点属性的内容到下个元素
void glVertexAttribDivisor (GLuint index, GLuint divisor);
// index 表示顶点属性的索引
// divisor 表示每 divisor 个实例更新下顶点属性到下个元素,默认为 0
java在十四混合的相同,C++基本与十三类似需要绘制多个立方体,只是之前都是用的普通绘制,该章节直接调用一次绘制方法。
顶点着色器
#version 300 es
precision mediump float;//默认float 片段着色器需要指定
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec2 a_texCoord;
layout(location = 2) in vec3 a_normal;
layout(location = 3) in vec3 offset;
uniform mat4 u_MVPMatrix;//坐标变化矩阵
uniform mat4 u_ModelMatrix;//光照模型
out vec3 normal;
out vec3 fragPos;
out vec2 v_texCoord;
void main()
{
gl_Position = u_MVPMatrix * (a_position + vec4(offset, 1.0));
fragPos = vec3(u_ModelMatrix * (a_position + vec4(offset, 1.0)));
normal = mat3(transpose(inverse(u_ModelMatrix))) * a_normal;
v_texCoord = a_texCoord;
}
片段着色器
#version 300 es
precision mediump float;
struct Light {
vec3 position;
vec3 direction;
vec3 color;
float cutOff;
float outerCutOff;
float constant;
float linear;
float quadratic;
};
in vec3 normal;
in vec3 fragPos;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;
uniform vec3 viewPos;
uniform Light light;
void main()
{
vec4 objectColor = texture(s_TextureMap, v_texCoord);
vec3 lightDir = normalize(light.position - fragPos);
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
float ambientStrength = 0.4;
vec3 ambient = ambientStrength * light.color;
vec3 norm = normalize(normal);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * light.color;
vec3 viewDir = normalize(viewPos - fragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
vec3 specular = spec * light.color;
float distance = length(light.position - fragPos);
float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
diffuse *= attenuation;
specular *= attenuation;
diffuse *= intensity;
specular*= intensity;
vec3 finalColor = (ambient + diffuse + specular) * vec3(objectColor);
outColor = vec4(finalColor, 1.0f);
}
Instance.h
//
// Created by CreatWall_zhouwen on 2023/5/22.
//
#ifndef TENINSTANCE_INSTANCE_H
#define TENINSTANCE_INSTANCE_H
#include
#include
#include
#include
#include
Instance.cpp
//
// Created by CreatWall_zhouwen on 2023/5/22.
//
#include "Instance.h"
#include "Util.h"
#include "GLUtil.h"
#include
Instance* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
GLfloat vertices[] = {
//position //texture coord //normal
-0.08f, -0.08f, -0.08f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.08f, -0.08f, -0.08f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.08f, 0.08f, -0.08f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f,
0.08f, 0.08f, -0.08f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f,
-0.08f, 0.08f, -0.08f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f,
-0.08f, -0.08f, -0.08f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
-0.08f, -0.08f, 0.08f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.08f, -0.08f, 0.08f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.08f, 0.08f, 0.08f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.08f, 0.08f, 0.08f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.08f, 0.08f, 0.08f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.08f, -0.08f, 0.08f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.08f, 0.08f, 0.08f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.08f, 0.08f, -0.08f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.08f, -0.08f, -0.08f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.08f, -0.08f, -0.08f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.08f, -0.08f, 0.08f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.08f, 0.08f, 0.08f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.08f, 0.08f, 0.08f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.08f, 0.08f, -0.08f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.08f, -0.08f, -0.08f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.08f, -0.08f, -0.08f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.08f, -0.08f, 0.08f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.08f, 0.08f, 0.08f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.08f, -0.08f, -0.08f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f,
0.08f, -0.08f, -0.08f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f,
0.08f, -0.08f, 0.08f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
0.08f, -0.08f, 0.08f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
-0.08f, -0.08f, 0.08f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f,
-0.08f, -0.08f, -0.08f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f,
-0.08f, 0.08f, -0.08f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.08f, 0.08f, -0.08f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.08f, 0.08f, 0.08f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.08f, 0.08f, 0.08f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.08f, 0.08f, 0.08f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.08f, 0.08f, -0.08f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
};
void Instance::CreateProgram(const char *ver, const char *frag) {
LOGD("CreateProgram Enter");
// 编译链接用于离屏渲染的着色器程序
program = CreateGLProgram(ver, frag, vertexShaderHandle, fragShaderHandle);
if (program == GL_NONE)
{
LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
return;
}
LOGD("CreateGLProgram Success");
m_SamplerLoc = glGetUniformLocation(program, "s_TextureMap");
m_MVPMatLoc = glGetUniformLocation(program, "u_MVPMatrix");
m_ModelMatrixLoc = glGetUniformLocation(program, "u_ModelMatrix");
m_ViewPosLoc = glGetUniformLocation(program, "viewPos");
//创建VBO
//创建实例化的VBO
glm::vec3 translations[125];
int index = 0;
GLfloat offset = 0.2f;
for(GLint y = -10; y < 10; y += 4)
{
for(GLint x = -10; x < 10; x += 4)
{
for(GLint z = -10; z < 10; z += 4)
{
glm::vec3 translation;
translation.x = (GLfloat)x / 10.0f + offset;
translation.y = (GLfloat)y / 10.0f + offset;
translation.z = (GLfloat)z / 10.0f + offset;
translations[index++] = translation;
}
}
}
glGenBuffers(2, m_VboId);
glBindBuffer(GL_ARRAY_BUFFER, m_VboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_VboId[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * 125, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//创建VAO绑定VAO和纹理
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_VboId[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (const void *) (3* sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (const void *) (5* sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindBuffer(GL_ARRAY_BUFFER, m_VboId[1]);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(3, 1);//告诉OPenGL这是一个实例化顶点属性 属性3是实例化顶点属性,并且告诉绘制一次就更新一次数组中的元素
glBindVertexArray(GL_NONE);
glGenTextures(1, &m_TextureId);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//绑定纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_vcImage[0].width, m_vcImage[0].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_vcImage[0].data);glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
}
void Instance::Draw() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearColor(0.2f, 0.9f, 0.3f, 1.0f);
// UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, (float) srceenWidth / srceenHeight);
glEnable(GL_DEPTH_TEST);//启用深度测试,注意glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);也要进行清除
glUseProgram(program);
glBindVertexArray(m_VaoId);
//绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);
float ratio = (float)srceenWidth / srceenHeight;
//设置参数
//glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glUniform3f(m_ViewPosLoc, 0.0f, 0.0f, 3.0f);
// 设置光源的位置、颜色和方向
glUniform3f(glGetUniformLocation(program, "light.position"), 0.0f, 0.0f, 3.0f);
glUniform3f(glGetUniformLocation(program, "light.color"), 1.0f, 1.0f, 1.0f);
glUniform3f(glGetUniformLocation(program, "light.direction"), 0.0f, 0.0f, -1.0f);
// 用于计算边缘的过度,cutOff 表示内切光角,outerCutOff 表示外切光角
glUniform1f(glGetUniformLocation(program, "light.cutOff"), glm::cos(glm::radians(10.5f)));
glUniform1f(glGetUniformLocation(program, "light.outerCutOff"), glm::cos(glm::radians(11.5f)));
// 衰减系数,常数项 constant,一次项 linear 和二次项 quadratic。
glUniform1f(glGetUniformLocation(program, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(program, "light.linear"), 0.09);
glUniform1f(glGetUniformLocation(program, "light.quadratic"), 0.032);
// 绘制多个立方体,不同的位移和旋转角度
UpdateMatrix(m_MVPMatrix, m_ModelMatrix, m_AngleX + 10, m_AngleY + 10, m_ScaleX > m_ScaleY ? m_ScaleY : m_ScaleX, glm::vec3(0.0f, 0.0f, 0.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glUniformMatrix4fv(m_ModelMatrixLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);
glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 125);//实例化绘制
glBindVertexArray(0);
}
void Instance::getTexturedata(std::vector vcImagetemp){
m_vcImage = vcImagetemp;
}
Instance *Instance::GetInstance() {
if (m_pContext == nullptr)
{
m_pContext = new Instance();
}
return m_pContext;
}
void Instance::DestroyInstance() {
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
void Instance::OnSurfaceChanged(int width, int height) {
glViewport(0, 0, width, height);
srceenWidth = width;
srceenHeight = height;
LOGD("OnSurfaceChanged Srceenwidth = %d, Srceenheight = %d, ratio = %f", width,height);
}
void Instance::UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY) {
m_AngleX = static_cast(rotateX);
m_AngleY = static_cast(rotateY);
m_ScaleX = 2*scaleX;
m_ScaleY = 2*scaleY;
}
void Instance::UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio) {
//No implement
}
void Instance::UpdateMatrix(glm::mat4 &mvpMatrix, glm::mat4 &modelMatrix, int angleXRotate,
int angleYRotate, float scale, glm::vec3 transVec3, float ratio) {
LOGD("DepthTestingSample::UpdateMatrix angleX = %d, angleY = %d, ratio = %f", angleXRotate,
angleYRotate, ratio);
angleXRotate = angleXRotate % 360;
angleYRotate = angleYRotate % 360;
//转化为弧度角
float radiansX = static_cast(MATH_PI / 180.0f * angleXRotate);
float radiansY = static_cast(MATH_PI / 180.0f * angleYRotate);
// Projection matrix
//glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f);
//glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f);
// View matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0, 0, 3), // Camera is at (0,0,1), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(scale, scale, scale));
Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
//Model = glm::translate(Model, transVec3);
modelMatrix = Model;
mvpMatrix = Projection * View * Model;
}
OpenGL ES 实例化(Instancing)是一种只调用一次渲染函数就能绘制出很多物体的技术,可以实现将数据一次性发送给 GPU ,避免了 CPU 多次向 GPU 下达渲染命令,提升了渲染性能。
而粒子系统本质上是通过一次或者多次渲染绘制出大量位置、形状或者颜色不同的物体(粒子),形成大量粒子运动的视觉效果。所以,粒子系统天然适合用OpenGL ES 实例化(Instancing)实现。
定义粒子,通常一个粒子有一个生命值,生命值结束该粒子消失,还有描述粒子在(x, y, z)三个方向的位置(偏移)和运动速度,以及粒子的颜色等属性.
struct Particle {
GLfloat dx,dy,dz;//粒子位置
GLfloat dxSpeed,dySpeed,dzSpeed;//粒子速度
GLubyte r,g,b,a; //粒子颜色
GLfloat life;//粒子生命值
Particle()//进行初始化
{
dx = 0.0f;
dy = 0.0f;
dz = 0.0f;
r = static_cast(1.0f);
g = static_cast(1.0f);
b = static_cast(1.0f);
a = static_cast(1.0f);
dxSpeed = 1.0f;
dySpeed = 1.0f;
dzSpeed = 1.0f;
life = 5.0f;
}
};
渲染粒子需要用到的顶点着色器
注意 有四个属性值 前两个与之前demo无差不别,是顶点坐标和纹理坐标,属性2,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_MVPMatrix;//旋转
out vec2 v_texCoord;
out vec4 v_color;//粒子颜色
void main()
{
gl_Position = u_MVPMatrix * vec4(a_vertex - vec3(0.0, 0.95, 0.0) + a_offset, 1.0);
v_texCoord = a_texCoord;
v_color = a_particlesColor;
}
因此在程序创建的时候多两个VBO
//创建VBO
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);
// 初始化一个空的数组,在最后更新的时候赋值
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
glGenBuffers(1, &m_ParticlesColorVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
// 初始化一个空的数组,在最后更新的时候赋值
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte), NULL, GL_DYNAMIC_DRAW);
//创建VAO并绑定VBO
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);
//坐标和纹理
glEnableVertexAttribArray(0);//启动数组属性0
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);//绑定m_ParticlesVertexVboId这个vbp
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);
//启动是否是实例化 只有2,3属性是实例化属性并且绘画一次就更新数据
glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 0);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glBindVertexArray(GL_NONE);
注意因为粒子在每次绘画时都会出现,则关于粒子位置和颜色的VBO数据时空的,在绘制的时候才会赋值。
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);//在这里再绑定数据 用来更新VBO中的部分数据
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);
片段着色器
#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;
}
直接在实例化工程中加入ParticlesDemo类,调用时只需要修改cmake和nactive.cpp的调用即可
ParticlesDemo.h
//
// Created by CreatWall_zhouwen on 2023/5/23.
//
#ifndef TENINSTANCE_PARTICLESDEMO_H
#define TENINSTANCE_PARTICLESDEMO_H
#include
#include
#include
#include
#include
ParticlesDemo.cpp
//
// Created by CreatWall_zhouwen on 2023/5/23.
//
#include "ParticlesDemo.h"
#include "Util.h"
#include "GLUtil.h"
#include
#include
#include
#include
ParticlesDemo* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
GLfloat g_vertex_buffer_data[] = {
//position //texture coord
-0.05f, -0.05f, -0.05f, 0.0f, 0.0f,
0.05f, -0.05f, -0.05f, 1.0f, 0.0f,
0.05f, 0.05f, -0.05f, 1.0f, 1.0f,
0.05f, 0.05f, -0.05f, 1.0f, 1.0f,
-0.05f, 0.05f, -0.05f, 0.0f, 1.0f,
-0.05f, -0.05f, -0.05f, 0.0f, 0.0f,
-0.05f, -0.05f, 0.05f, 0.0f, 0.0f,
0.05f, -0.05f, 0.05f, 1.0f, 0.0f,
0.05f, 0.05f, 0.05f, 1.0f, 1.0f,
0.05f, 0.05f, 0.05f, 1.0f, 1.0f,
-0.05f, 0.05f, 0.05f, 0.0f, 1.0f,
-0.05f, -0.05f, 0.05f, 0.0f, 0.0f,
-0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
-0.05f, 0.05f, -0.05f, 1.0f, 1.0f,
-0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
-0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
-0.05f, -0.05f, 0.05f, 0.0f, 0.0f,
-0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
0.05f, 0.05f, -0.05f, 1.0f, 1.0f,
0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
0.05f, -0.05f, 0.05f, 0.0f, 0.0f,
0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
-0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
0.05f, -0.05f, -0.05f, 1.0f, 1.0f,
0.05f, -0.05f, 0.05f, 1.0f, 0.0f,
0.05f, -0.05f, 0.05f, 1.0f, 0.0f,
-0.05f, -0.05f, 0.05f, 0.0f, 0.0f,
-0.05f, -0.05f, -0.05f, 0.0f, 1.0f,
-0.05f, 0.05f, -0.05f, 0.0f, 1.0f,
0.05f, 0.05f, -0.05f, 1.0f, 1.0f,
0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
0.05f, 0.05f, 0.05f, 1.0f, 0.0f,
-0.05f, 0.05f, 0.05f, 0.0f, 0.0f,
-0.05f, 0.05f, -0.05f, 0.0f, 1.0f,
};
void ParticlesDemo::CreateProgram(const char *ver, const char *frag) {
LOGD("CreateProgram Enter");
// 编译链接用于离屏渲染的着色器程序
program = CreateGLProgram(ver, frag, vertexShaderHandle, fragShaderHandle);
if (program == GL_NONE)
{
LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
return;
}
LOGD("CreateGLProgram Success");
m_SamplerLoc = glGetUniformLocation(program, "s_TextureMap");
m_MVPMatLoc = glGetUniformLocation(program, "u_MVPMatrix");
m_pParticlesPosData = new GLfloat[MAX_PARTICLES * 3];
m_pParticlesColorData = new GLubyte[MAX_PARTICLES * 4];
for (int i = 0; i < MAX_PARTICLES; i++)
{
GenerateNewParticle(m_ParticlesContainer[i]);
}
//创建VBO
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);
// 初始化一个空的数组,在最后更新的时候赋值
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
glGenBuffers(1, &m_ParticlesColorVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
// 初始化一个空的数组,在最后更新的时候赋值
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte), NULL, GL_DYNAMIC_DRAW);
//创建VAO并绑定VBO
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);
//坐标和纹理
glEnableVertexAttribArray(0);//启动数组属性0
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);//绑定m_ParticlesVertexVboId这个vbp
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);
//启动是否是实例化 只有2,3属性是实例化属性并且绘画一次就更新数据
glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 0);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glBindVertexArray(GL_NONE);
glGenTextures(1, &m_TextureId);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//绑定纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_vcImage[0].width, m_vcImage[0].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_vcImage[0].data);glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
}
void ParticlesDemo::Draw() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearColor(0.2f, 0.9f, 0.3f, 1.0f);
// UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, (float) srceenWidth / srceenHeight);
glEnable(GL_DEPTH_TEST);//启用深度测试,注意glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);也要进行清除
glDisable(GL_BLEND);//禁止混合
UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, (float) srceenWidth / srceenHeight);
//每次获取生命值大于 0 粒子的数量
int particleCount = UpdateParticles();//更新粒子数据
glUseProgram(program);
glBindVertexArray(m_VaoId);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawArraysInstanced(GL_TRIANGLES, 0, 36, particleCount);
}
void ParticlesDemo::getTexturedata(std::vector vcImagetemp) {
m_vcImage = vcImagetemp;
}
ParticlesDemo *ParticlesDemo::GetInstance() {
if (m_pContext == nullptr)
{
m_pContext = new ParticlesDemo();
}
return m_pContext;
}
void ParticlesDemo::DestroyInstance() {
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
void ParticlesDemo::OnSurfaceChanged(int width, int height) {
glViewport(0, 0, width, height);
srceenWidth = width;
srceenHeight = height;
LOGD("OnSurfaceChanged Srceenwidth = %d, Srceenheight = %d, ratio = %f", width,height);
}
void ParticlesDemo::UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY) {
m_AngleX = static_cast(rotateX);
m_AngleY = static_cast(rotateY);
m_ScaleX = scaleX;
m_ScaleY = scaleY;
}
void ParticlesDemo::UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio) {
LOGD("ParticlesSample::UpdateMVPMatrix angleX = %d, angleY = %d, ratio = %f", angleX,
angleY, ratio);
angleX = angleX % 360;
angleY = angleY % 360;
//转化为弧度角
float radiansX = static_cast(MATH_PI / 180.0f * angleX);
float radiansY = static_cast(MATH_PI / 180.0f * angleY);
// Projection matrix
//glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f);
//glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f);
// View matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0, 6, 0), // Camera is at (0,0,1), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 0, 1) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(m_ScaleX, m_ScaleX, m_ScaleX));
Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
Model = glm::translate(Model, glm::vec3(0.0f, 0.0f, 0.0f));
mvpMatrix = Projection * View * Model;
}
void ParticlesDemo::UpdateMatrix(glm::mat4 &mvpMatrix, glm::mat4 &modelMatrix, int angleXRotate,
int angleYRotate, float scale, glm::vec3 transVec3, float ratio) {
LOGD("DepthTestingSample::UpdateMatrix angleX = %d, angleY = %d, ratio = %f", angleXRotate,
angleYRotate, ratio);
angleXRotate = angleXRotate % 360;
angleYRotate = angleYRotate % 360;
//转化为弧度角
float radiansX = static_cast(MATH_PI / 180.0f * angleXRotate);
float radiansY = static_cast(MATH_PI / 180.0f * angleYRotate);
// Projection matrix
//glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f);
//glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f);
// View matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0, 0, 3), // Camera is at (0,0,1), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(scale, scale, scale));
Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
//Model = glm::translate(Model, transVec3);
modelMatrix = Model;
mvpMatrix = Projection * View * Model;
}
int ParticlesDemo::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;
}
/*void ParticlesDemo::SortParticles() {
std::sort(&m_ParticlesContainer[0], &m_ParticlesContainer[MAX_PARTICLES]);
}*/
int ParticlesDemo::UpdateParticles() {
每次生成 300 个新粒子,产生爆炸的效果
int newParticles = 300;
for (int i = 0; i < newParticles; i++)
{
int particleIndex = FindUnusedParticle();
if (particleIndex >= 0)
{
GenerateNewParticle(m_ParticlesContainer[particleIndex]);
}
}
// 生命值大于 0 的粒子进行更新
int particlesCount = 0;
for (int i = 0; i < MAX_PARTICLES; i++)
{
Particle &p = m_ParticlesContainer[i]; // shortcut
if (p.life > 0.0f)
{
float delta = 0.1f;
// Decrease life
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.081f, 0.0f) * delta * 0.3f;
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++;
}
}
//SortParticles();
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);//在这里再绑定数据 用来更新VBO中的部分数据
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;
}
void ParticlesDemo::GenerateNewParticle(Particle &particle) {
particle.life = 5.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(rand() % 256);
particle.g = static_cast(rand() % 256);
particle.b = static_cast(rand() % 256);
particle.a = static_cast((rand() % 256) / 3);
}