十五、实例化

news2025/2/24 18:02:35

第一部分 概念:

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 <GLES3/gl3.h>
#include <detail/type_mat.hpp>
#include <detail/type_mat4x4.hpp>
#include <vector>
#include <map>
#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<struct ImageTyep> 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<struct ImageTyep> 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 <gtc/matrix_transform.hpp>
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<struct ImageTyep> 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<int>(rotateX);
    m_AngleY = static_cast<int>(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<float>(MATH_PI / 180.0f * angleXRotate);
    float radiansY = static_cast<float>(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<GLubyte>(1.0f);
        g = static_cast<GLubyte>(1.0f);
        b = static_cast<GLubyte>(1.0f);
        a = static_cast<GLubyte>(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 <GLES3/gl3.h>
#include <detail/type_mat.hpp>
#include <detail/type_mat4x4.hpp>
#include <vector>
#include <map>
#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<GLubyte>(1.0f);
        g = static_cast<GLubyte>(1.0f);
        b = static_cast<GLubyte>(1.0f);
        a = static_cast<GLubyte>(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<struct ImageTyep> 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<struct ImageTyep> 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 <gtc/matrix_transform.hpp>
#include <ctime>
#include <cstdlib>
#include <algorithm>
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<struct ImageTyep> 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<int>(rotateX);
    m_AngleY = static_cast<int>(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<float>(MATH_PI / 180.0f * angleX);
    float radiansY = static_cast<float>(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<float>(MATH_PI / 180.0f * angleXRotate);
    float radiansY = static_cast<float>(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<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);
}

#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/619220.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

练习:有限状态机测试

练习&#xff1a;有限状态机测试 1 FSM 示例 在练习中&#xff0c;我们将使用两个 FSM。 两者都有输入字母 X {a, b} 和输出字母 Y {0,1}。 第一个 FSM 将称为 M1 并由以下有向图表示。 对于上面给出的每个 FSM Mi&#xff1a; 1.确定以下值&#xff0c;显示您的工作。 (a…

[MySQL]不就是SQL语句

前言 本期主要的学习目标是SQl语句中的DDL和DML实现对数据库的操作和增删改功能&#xff0c;学习完本章节之后需要对SQL语句手到擒来。 1.SQL语句基本介绍 SQL&#xff08;Structured Query Language&#xff09;是一种用于管理关系型数据库的编程语言。它允许用户在数据库中存…

AngularJs学习笔记--unit-testing

javascript是一门动态类型语言&#xff0c;这给她带来了很强的表现能力&#xff0c;但同时也使编译器几乎不能给开发者提供任何帮助。因为这个原因&#xff0c;我们感受到编写任何javascript代码都必须有一套强大完整的测试。angular拥有许多功能&#xff0c;让我们更加容易地测…

如何编写接口测试用例?测试工程师必备技能!

自动化始终只是辅助测试工作的一个手段&#xff0c;对于测试人员而言&#xff0c;测试基础和测试用例的设计才是核心。如果测试用例的覆盖率或者质量不高&#xff0c;那将这部分用例实现为自动化用例的意义也就不大了。 那么&#xff0c;接口测试用例应该怎么编写呢&#xff1f…

基于SpringBoot实现的分页查询(分分钟钟上手)

这里是使用的hibernate(不需要写sql)和springboot 也可使用 MyBatis&#xff08;推荐使用&#xff09; 下面是使用Spring Boot实现分页查询的示例&#xff1a; 在pom.xml文件中添加依赖项&#xff1a; <dependency><groupId>org.projectlombok</groupId>&l…

阿里面经最新分享:Java 面试指南 / 成长笔记(程序员面试必备)

写在前面 又到了收割 Offer 的季节&#xff0c;你准备好了吗&#xff1f;曾经的我&#xff0c;横扫各个大厂的 Offer。还是那句话&#xff1a;进大厂临时抱佛脚是肯定不行的&#xff0c;一定要注重平时的总结和积累&#xff0c;多思考&#xff0c;多积累&#xff0c;多总结&am…

2023年牛客网互联网高级架构师Java面试八股汇总(附答案整理)

此文包含 Java 面试的各个方面&#xff0c;史上最全&#xff0c;苦心整理最全 Java 面试题目整理包括基础JVM算法数据库优化算法数据结构分布式并发编程缓存等&#xff0c;使用层面广&#xff0c;知识量大&#xff0c;涉及你的知识盲点。要想在面试者中出类拔萃就要比人付出更多…

【动态规划】-最小路径和(java)

最小路劲和--动态规划和内存压缩 最小路径和题目描述 动态规划解题思路&#xff1a;代码演示动态规划的内存压缩动态规划专题 最小路径和 题目描述 给定一个二维数组matrix&#xff0c;一个人必须从左上角出发&#xff0c;最后到达右下角 沿途只可以向下或者向右走&#xff0c…

用了7年的 source insight 3.5 背景色及字体配置(提升code效率)

文章目录 背景目的背景护眼色配置字体与编辑选项配置全局配置的保存与加载 背景 今天突然source insight3.5抽风&#xff0c;配置重置了&#xff0c;默认配置很难看。 找了一会儿才找到之前的备份配置&#xff0c;load后舒服了。 目的 在此记录并share用了7年的 source insi…

300页幻灯片图解数据安全风险评估(附下载)

为指导网络数据安全风险评估工作&#xff0c;发现数据安全隐患&#xff0c;防范数据安全风险,依据《中华人民共和国网络安全法》《中华人民共和国数据安全法》《中华人民共和国个人信息保护法》等法律法规&#xff0c;参照数据安全相关国家标准&#xff0c;全国信息安全标准化技…

ChatGPT与VBA:Excel操作一键搞定的全能助手

摘要 Excel是一款广泛应用于数据处理和分析的工具&#xff0c;而VBA&#xff08;Visual Basic for Applications&#xff09;是一种用于编程自动化Excel操作的语言。然而&#xff0c;对于非专业的Excel用户来说&#xff0c;VBA编程可能具有一定的难度。本文将探讨如何利用Chat…

不同规格的磁场线圈适用领域

磁场线圈基于毕奥-萨法尔定律&#xff0c;以绕组中通电流的形式复现磁场的线圈&#xff0c;用于复现标准磁场&#xff0c;是弱磁场计量测试领域最主要的标准器具之一。按所复现的磁场类型可分为恒定磁场线圈、交变磁场线圈、梯度磁场线圈、脉冲磁场线圈等&#xff0c;按结构可分…

AIX5.3安装Python3遇到的坑

最近在折腾AIX5.3&#xff0c;这是一款非常古老的power架构的服务器&#xff0c;目前IBM官方已经不维护&#xff0c;但是仍然在很多银行里跑着比较核心的业务&#xff0c;由于我们的客户大部分是面向银行&#xff0c;少不了要做AIX5.3上的应用软件适配。本文记录在AIX5.3上编译…

51单片机——DS18B20 温度传感器实验,小白讲解,相互学习

DS18B20 介绍 DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线&#xff08;单总线&#xff09;”接 口的温度传感器。与传统的热敏电阻等测温元件相比&#xff0c;它是一种新型的体积小、 适用电压宽、与微处理器接口简单的数字化温度传感器。 DS18B20 温度传感器具有如…

linuxOPS基础_linux常用自有服务(ntp,firewalld,crond)

linux常用自有服务 服务名含义ntpd用于同步计算机的系统时间的服务firewalld防火墙服务crond计划任务服务 ntp时间同步服务 NTP是网络时间协议(Network Time Protocol)&#xff0c;它是用来同步网络中各个计算机的时间的协议。 工作场景&#xff1a; ​ 公司开发了一个电商…

Jmeter教程 —— 简单的压力测试

Jmeter是一个非常好用的压力测试工具&#xff0c;可以用来做轻量级的压力测试&#xff0c;非常合适&#xff0c;只需要十几分钟&#xff0c;就能把压力测试需要的脚本写好。 什么是压力测试 顾名思义&#xff1a;压力测试&#xff0c;就是 被测试的系统&#xff0c;在一定的…

Android Studio查看第三方库依赖树

在Android开发过程中&#xff0c;我们或多或少都会引入第三方库&#xff0c;引入的库越多&#xff0c;越容易产生库之间的依赖冲突&#xff0c;所以我们今天一起来了解Android Studio查看第三方库依赖树。 下面就拿我遇到的问题还原一下&#xff1a; 之前接人容联客服系统的时候…

工作小技巧,封装if和Exception

在我们的工作中&#xff0c;经常会有这样的逻辑&#xff0c;就是判断一个参数是否合法&#xff0c;不合法就抛出异常&#xff0c;再由全局异常捕获后返回。 本文使用我编写的springboot日记本系统作为蓝本&#xff0c;&#xff0c;在已有项目的基础上改&#xff0c;源码下载地…

思科小型网络配置实例

一、配置思路 1.交换机上配置端口连接描述&#xff0c;在实际工作中很有用 2.外网配置&#xff0c;模拟外网&#xff1a;PC的IP地址200.200.200.2&#xff0c;Server的IP地址是200.200.200.3&#xff0c;网关都是200.200.200.1。SW-ISP的连接PC和Server-ISP的口属于vlan 3,&…

【vue】二:核心处理---vue的生命周期

文章目录 1.Vue生命周期的四个阶段&#xff0c;八个钩子2.vue的生命周期图例3. 初始阶段&#xff1a;虚拟DOM生成4. 挂载阶段&#xff1a;真实DOM生成5. 更新阶段&#xff1a;data变化重新渲染6. 销毁阶段&#xff1a;卸载所有&#xff0c;销毁vm 1.Vue生命周期的四个阶段&…