十五、实例化

第一部分 概念:

1)引入

OpenGL ES 实例化(Instancing)是一种只调用一次渲染函数就能绘制出很多物体的技术,可以实现将数据一次性发送给 GPU ,告诉 OpenGL ES 使用一个绘制函数,将这些数据绘制成多个物体。

实例化(Instancing)避免了 CPU 多次向 GPU 下达渲染命令(避免多次调用 glDrawArrays 或 glDrawElements 等绘制函数),节省了绘制多个物体时 CPU 与 GPU 之间的通信时间,提升了渲染性能。

2)应用

对于使用,实例化调用和普通调用

相对于普通绘制,实例化绘制多了一个参数 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 
#include "Const.h"
#define MATH_PI 3.1415926535897932384626433832802
class Instance {
public:
    Instance(){
        program = 0;
        vertexShaderHandle = 0;
        fragShaderHandle = 0;
        m_ModelMatrix = glm::mat4(0.0f);
        m_AngleX = 0;
        m_AngleY = 0;
        m_ScaleX = 1.0f;
        m_ScaleY = 1.0f;
        m_ModelMatrix = glm::mat4(0.0f);
    };
    ~Instance(){};
    void CreateProgram(const char *ver, const char *frag);
    void Draw();
    void getTexturedata(std::vector vcImagetemp);
    static Instance* GetInstance();
    static void DestroyInstance();
    void OnSurfaceChanged(int width, int height);
    void UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);
    void UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio);
    void UpdateMatrix(glm::mat4 &mvpMatrix, glm::mat4 &modelMatrix, int angleXRotate, int angleYRotate, float scale, glm::vec3 transVec3, float ratio);

private:
    GLuint program;
    GLuint vertexShaderHandle;
    GLuint fragShaderHandle;
    GLuint m_VaoId;//存放顶点数据
    GLuint m_VaoIdInstance;//存放顶点数据
    GLuint m_VboId[2];//顶点缓冲区
    unsigned int m_TextureId;
    unsigned char *texturedata;
    int texturewidth, textureheight;

    int srceenWidth, srceenHeight;//屏幕宽高
    GLint m_SamplerLoc;
    GLint m_MVPMatLoc;
    GLint m_ModelMatrixLoc;
    GLint m_LightPosLoc;
    GLint m_LightColorLoc;
    GLint m_ViewPosLoc;

    glm::mat4 m_MVPMatrix;
    glm::mat4 m_ModelMatrix;
    int m_AngleX;
    int m_AngleY;
    float m_ScaleX;
    float m_ScaleY;

    std::vector m_vcImage;
};


#endif //TENINSTANCE_INSTANCE_H

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;
}

第三部分 粒子实践

1)概念

OpenGL ES 实例化(Instancing)是一种只调用一次渲染函数就能绘制出很多物体的技术,可以实现将数据一次性发送给 GPU ,避免了 CPU 多次向 GPU 下达渲染命令,提升了渲染性能。

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

2)应用细节

定义粒子,通常一个粒子有一个生命值,生命值结束该粒子消失,还有描述粒子在(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;
}

3)代码

直接在实例化工程中加入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 
#include "Const.h"
#define MATH_PI 3.1415926535897932384626433832802
#define MAX_PARTICLES 500
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;
    }
};

class ParticlesDemo {
public:
    ParticlesDemo(){
        program = 0;
        vertexShaderHandle = 0;
        fragShaderHandle = 0;
        m_ModelMatrix = glm::mat4(0.0f);
        m_AngleX = 0;
        m_AngleY = 0;
        m_ScaleX = 1.0f;
        m_ScaleY = 1.0f;
        m_ModelMatrix = glm::mat4(0.0f);
    };
    ~ParticlesDemo(){};
    void CreateProgram(const char *ver, const char *frag);
    void Draw();
    void getTexturedata(std::vector vcImagetemp);
    static ParticlesDemo* GetInstance();
    static void DestroyInstance();
    void OnSurfaceChanged(int width, int height);
    void UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);
    void UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio);
    void UpdateMatrix(glm::mat4 &mvpMatrix, glm::mat4 &modelMatrix, int angleXRotate, int angleYRotate, float scale, glm::vec3 transVec3, float ratio);

    int FindUnusedParticle();
    //void SortParticles();
    int UpdateParticles();
    void GenerateNewParticle(Particle &particle);
private:
    GLuint program;
    GLuint vertexShaderHandle;
    GLuint fragShaderHandle;
    GLuint m_VaoId;
    GLuint m_ParticlesVertexVboId;
    GLuint m_ParticlesPosVboId;
    GLuint m_ParticlesColorVboId;
    unsigned int m_TextureId;
    unsigned char *texturedata;
    int texturewidth, textureheight;
    GLint m_SamplerLoc;
    GLint m_MVPMatLoc;

    int srceenWidth, srceenHeight;//屏幕宽高
    glm::mat4 m_MVPMatrix;
    glm::mat4 m_ModelMatrix;
    int m_AngleX;
    int m_AngleY;
    float m_ScaleX;
    float m_ScaleY;

    std::vector m_vcImage;
    //粒子相关
    Particle m_ParticlesContainer[MAX_PARTICLES];//每个粒子的属性值,
    GLfloat* m_pParticlesPosData;//存放位置
    GLubyte* m_pParticlesColorData;//颜色
    int m_LastUsedParticle;
};


#endif //TENINSTANCE_PARTICLESDEMO_H

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);
}

十五、实例化_第1张图片

你可能感兴趣的:(OpenGL,安卓学习,opengles实例化粒子化)