Efectos de partículas Android OpenGL ES 3.0

1. Efectos de partículas

Efectos de partículas: Efecto de partículas, los efectos de partículas esencialmente dibujan una gran cantidad de objetos (partículas) con diferentes posiciones, formas o colores a través de una o más representaciones, formando un efecto visual de una gran cantidad de movimientos de partículas. Por lo tanto, los efectos de partículas son naturalmente adecuados para la implementación con creación de instancias de OpenGL ES.

2. Definir partículas

Defina partículas, generalmente una partícula tiene un valor de vida, la partícula desaparece cuando termina el valor de vida, y describe la posición (desplazamiento) y la velocidad de movimiento de la partícula en las tres direcciones (x, y, z), así como el color de la partícula y otros atributos. Las partículas se pueden definir como una estructura:

struct Particle {
    GLfloat dxSpeed,dySpeed,dzSpeed;// 粒子速度
    GLubyte r,g,b,a; //r,g,b,a      //粒子颜色
    GLfloat dx,dy,dz; // 粒子的位置
    GLfloat life;               //粒子出现的时长
    GLfloat cameraDistance;

    Particle(){

        dxSpeed = 1.0f;
        dySpeed = 1.0f;
        dzSpeed = 1.0f;

        dx = 0.0f;
        dy = 0.0f;
        dz = 0.0f;
        
        r = static_cast<GLubyte>(1.0f);
        g = static_cast<GLubyte>(1.0f);
        b = static_cast<GLubyte>(1.0f);
        a = static_cast<GLubyte>(1.0f);

        life = 10.0f;  //粒子的生命值
    }
    /*操作符重载 < */
    bool operator<(const Particle& that) const {
        return this->cameraDistance > that.cameraDistance;
    }
};

3. Sombreador de vértices

#version 300 es
precision mediump float;
layout(location = 0) in vec3 a_vertex;          //粒子顶点
layout(location = 1) in vec2 a_texCoord;        //粒子材质
layout(location = 2) in vec3 a_offset;          //粒子位置
layout(location = 3) in vec4 a_particlesColor;    //粒子颜色

uniform mat4 u_mat;    //MVP矩阵   观察矩阵*模型矩阵*透视矩阵

/*输出的材质和颜色*/
out vec2 v_texCoord;
out vec4 v_color;

void main() {
    gl_Position = u_mat * vec4(a_vertex - vec3(0.0, 0.95, 0.0) + a_offset, 1.0);
    v_texCoord = a_texCoord;
    v_color = a_particlesColor;
}

El atributo a_offsetes el desplazamiento de posición de la partícula, que en última instancia determina la posición de la partícula. El atributo a_particlesColorrepresenta el color de la luz que brilla en la superficie de la partícula. Ambos atributos son matrices instanciadas, porque cada partícula tiene una posición y un color diferentes. .

Pase los atributos relacionados con las partículas, la matriz mvp, haga cálculos simples en la posición y pase el color y el material al fragment shader

4. Sombreador de fragmentos

#version 300 es
precision mediump float;
in vec2 v_texCoord;
in vec4 v_color;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;


void main() {
    outColor = texture(s_TextureMap, v_texCoord) * v_color;
//    outColor = texture(s_TextureMap, v_texCoord) ;
}

Si la partícula quiere mostrar el material en sí, puede asignar directamente el color obtenido por la muestra a outColor sin multiplicarlo con v_color

5. Análisis del código del proceso de implementación del programa

5.1 Preparación antes del dibujo
glGenBuffers(1, &m_ParticlesVertexVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
             g_vertex_buffer_data,
             GL_STATIC_DRAW);

glGenBuffers(1, &m_ParticlesPosVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat),
             NULL, GL_DYNAMIC_DRAW);

glGenBuffers(1, &m_ParticlesColorVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte),
             NULL, GL_DYNAMIC_DRAW);

Cree un VBO, tenga en cuenta: el vértice de la partícula es GL_STATIC_DRAW, la posición y el color cambiarán dinámicamente, por lo que es GL_DYNAMIC_DRAW

// Generate VAO Id
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);

/*给shader传递数据*/
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glVertexAttribPointer(
        0,
        3,
        GL_FLOAT,
        GL_FALSE,
        5 * sizeof(GLfloat),
        (void *) 0
);

glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesVertexVboId);
glVertexAttribPointer(
        1,
        2,
        GL_FLOAT,
        GL_FALSE,
        5 * sizeof(GLfloat),
        (const void *) (3 * sizeof(GLfloat))
);


glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);
glVertexAttribPointer(
        2,
        3,
        GL_FLOAT,
        GL_FALSE,
        0,
        (void *) 0
);

glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
glVertexAttribPointer(
        3,
        4,
        GL_UNSIGNED_BYTE,
        GL_TRUE,
        0,
        (void *) 0
);
  • Crear y vincular un VAO

  • Asigne valores a las variables a_vertex, a_texCoord, a_offset y a_particlesColor en el shader

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

glVertexAttribDivisor(0, 0);Indica que el atributo con índice = 0 no se actualiza cuando se dibuja la instancia; glVertexAttribDivisor(2, 1);el atributo utilizado para especificar índice = 2 es una matriz instanciada, y 1 significa que cada vez que se dibuja una instancia, los elementos de la matriz se actualizan una vez.

5.2 Realizar operaciones de dibujo
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_BLEND); //关闭混合

//    value += 1;
//    value = value % 360;
//    value = value % 360;

    //转化为弧度角
    float radiansX = static_cast<float>(MATH_PI / 180.0f * value);

    glm::mat4 modelMat = glm::mat4(1.0f);  //模型矩阵
    modelMat = glm::scale(modelMat, glm::vec3(0.8f, 0.8f, 0.8f));
    modelMat = glm::rotate(modelMat, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
    modelMat = glm::rotate(modelMat, radiansX, glm::vec3(0.0f, 1.0f, 0.0f));
    modelMat = glm::translate(modelMat, glm::vec3(0.0f, 0.0f, 0.0f));

    glm::mat4 projection =glm::perspective(45.0f, 0.5f, 0.1f, 100.f);

    // viewMat matrix
    glm::mat4 viewMat = 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)
    );


    glm::mat4 mvpMatrix = projection * viewMat * modelMat;
  • Se limpiaron los búferes de profundidad y color.
  • Crear matriz modelo
  • Crear una matriz de perspectiva
  • Crear matriz de observadores
  • Generar matriz MVP
int particleCount = UpdateParticles();

m_pOpenGLShader->Bind();
glBindVertexArray(m_VaoId);

m_pOpenGLShader->SetUniformValue("u_mat",mvpMatrix);

// Bind the RGBA map
m_pOpenGLShader->SetUniformValue("s_TextureMap",0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texID[3]);

glDrawArraysInstanced(GL_TRIANGLES, 0, 36, particleCount);
  • actualizar información de partículas
  • Pase la matriz MVP a Shader
  • Inicializar el muestreador
  • Instanciar para dibujar
void MSParticlesSample::GenerateNewParticle(Particle &particle) {
    particle.life = 10.0f;
    particle.cameraDistance = -1.0f;
    particle.dx = (rand() % 2000 - 1000.0f) / 3000.0f;
    particle.dy = (rand() % 2000 - 1000.0f) / 3000.0f;
    particle.dz = (rand() % 2000 - 1000.0f) / 3000.0f;

    float spread = 1.5f;

    glm::vec3 maindir = glm::vec3(0.0f, 2.0f, 0.0f);
    glm::vec3 randomdir = glm::vec3(
            (rand() % 2000 - 1000.0f) / 1000.0f,
            (rand() % 2000 - 1000.0f) / 1000.0f,
            (rand() % 2000 - 1000.0f) / 1000.0f
    );

    glm::vec3 speed = maindir + randomdir * spread;
    particle.dxSpeed = speed.x;
    particle.dySpeed = speed.y;
    particle.dzSpeed = speed.z;

    particle.r = static_cast<unsigned char>(rand() % 256);
    particle.g = static_cast<unsigned char>(rand() % 256);
    particle.b = static_cast<unsigned char>(rand() % 256);
    particle.a = static_cast<unsigned char>((rand() % 256) / 3);

}

La velocidad, el desplazamiento y el color de las nuevas partículas se generan aleatoriamente

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

    for (int i = 0; i < m_LastUsedParticle; i++)
    {
        if (m_ParticlesContainer[i].life <= 0)
        {
            m_LastUsedParticle = i;
            return i;
        }
    }

    return -1;
}

Encuentra partículas cuya salud haya terminado

int MSParticlesSample::UpdateParticles() {
    int newParticles = 300;
    for (int i = 0; i < newParticles; i++)
    {
        int particleIndex = FindUnusedParticle();
        if (particleIndex >= 0)
        {
            GenerateNewParticle(m_ParticlesContainer[particleIndex]);
        }
    }


    int particlesCount = 0;
    for (int i = 0; i < MAX_PARTICLES; i++)
    {

        Particle &p = m_ParticlesContainer[i]; // shortcut

        if (p.life > 0.0f)
        {
            float delta = 0.1f;
        
            glm::vec3 speed = glm::vec3(p.dxSpeed, p.dySpeed, p.dzSpeed), pos = glm::vec3(p.dx,
                                                                                          p.dy,
                                                                                          p.dz);
            p.life -= delta;
            if (p.life > 0.0f)
            {

              
                //模拟简单的物理:只有重力,没有碰撞
                speed += glm::vec3(0.0f, 0.81f, 0.0f) * delta * 0.5f;
                pos += speed * delta;

                p.dxSpeed = speed.x;
                p.dySpeed = speed.y;
                p.dzSpeed = speed.z;

                p.dx = pos.x;
                p.dy = pos.y;
                p.dz = pos.z;

                m_pParticlesPosData[3 * particlesCount + 0] = p.dx;
                m_pParticlesPosData[3 * particlesCount + 1] = p.dy;
                m_pParticlesPosData[3 * particlesCount + 2] = p.dz;

                m_pParticlesColorData[4 * particlesCount + 0] = p.r;
                m_pParticlesColorData[4 * particlesCount + 1] = p.g;
                m_pParticlesColorData[4 * particlesCount + 2] = p.b;
                m_pParticlesColorData[4 * particlesCount + 3] = p.a;

            }

            particlesCount++;

        }
    }

    glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesPosVboId);
    glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 3 * sizeof(GLfloat), NULL,
                 GL_DYNAMIC_DRAW); 

    glBufferSubData(GL_ARRAY_BUFFER, 0, particlesCount * sizeof(GLfloat) * 3, m_pParticlesPosData);

    glBindBuffer(GL_ARRAY_BUFFER, m_ParticlesColorVboId);
    glBufferData(GL_ARRAY_BUFFER, MAX_PARTICLES * 4 * sizeof(GLubyte), NULL,
                 GL_DYNAMIC_DRAW); 
    
    glBufferSubData(GL_ARRAY_BUFFER, 0, particlesCount * sizeof(GLubyte) * 4,
                    m_pParticlesColorData);

    return particlesCount;

}

Actualice la partícula (actualice la posición de la partícula, la velocidad de movimiento y el valor de vida) y luego actualice la matriz instanciada

Guess you like

Origin blog.csdn.net/u014078003/article/details/127982439