无法将粒子系统带入3D场景

时间:2019-04-09 05:40:03

标签: rendering glfw opengl-3 particles particle-system

我一直在学习this课程,以实现粒子系统。 试图将粒子系统引入3D场景。

我的入口点和初始化看起来像:

bool initOpenGL()
{
    // Intialize GLFW
    // GLFW is configured.  Must be called before calling any GLFW functions
    if (!glfwInit())
    {
        // An error occured
        std::cerr << "GLFW initialization failed" << std::endl;
        return false;
    }
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // forward compatible with newer versions of OpenGL as they become available but not backward compatible (it will not run on devices that do not support OpenGL 3.3

    // Create an OpenGL 3.3 core, forward compatible context window
    gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, APP_TITLE, NULL, NULL);
    if (gWindow == NULL)
    {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return false;
    }

    // Make the window's context the current one
    glfwMakeContextCurrent(gWindow);

    // Initialize GLEW
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK)
    {
        std::cerr << "Failed to initialize GLEW" << std::endl;
        return false;
    }

    // Set the required callback functions
    glfwSetKeyCallback(gWindow, glfw_onKey);
    glfwSetFramebufferSizeCallback(gWindow, glfw_onFramebufferSize);
    glfwSetScrollCallback(gWindow, glfw_onMouseScroll);

    glClearColor(gClearColor.r, gClearColor.g, gClearColor.b, gClearColor.a);

    // Define the viewport dimensions
    glViewport(0, 0, gWindowWidth, gWindowHeight);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    return true;
}

尝试在同一场景中同时渲染粒子系统和由网格组成的3D场景:

glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);
    programID = LoadShaders("shaders/Particle.vertexshader", "shaders/Particle.fragmentshader");
    CameraRight_worldspace_ID = glGetUniformLocation(programID, "CameraRight_worldspace");
    CameraUp_worldspace_ID = glGetUniformLocation(programID, "CameraUp_worldspace");
    ViewProjMatrixID = glGetUniformLocation(programID, "VP");
    TextureID = glGetUniformLocation(programID, "myTextureSampler");
    for (int i = 0; i < MaxParticles; i++)
    {
        ParticlesContainer[i].life = -1.0f;
        ParticlesContainer[i].cameradistance = -1.0f;
    }
    Texture = loadDDS("textures/particle.DDS");
    glGenBuffers(1, &billboard_vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    glGenBuffers(1, &particles_position_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
    // Initialize with empty (NULL) buffer : it will be updated later, each frame.
    glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

    // The VBO containing the colors of the particles
    glGenBuffers(1, &particles_color_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
    // Initialize with empty (NULL) buffer : it will be updated later, each frame.
    glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW);
while (!glfwWindowShouldClose(gWindow))
{
    showFPS(gWindow);

    double currentTime = glfwGetTime();
    double deltaTime = currentTime - lastTime;

    // Poll for and process events
    glfwPollEvents();
    update(deltaTime);

    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glm::mat4 model(1.0), view(1.0), projection(1.0);

    // Create the View matrix
    view = fpsCamera.getViewMatrix();
    glm::mat4 ViewMatrix = view;
    // Create the projection matrix
    projection = glm::perspective(glm::radians(fpsCamera.getFOV()), (float)gWindowWidth / (float)gWindowHeight, 0.1f, 200.0f);

    // update the view (camera) position
    glm::vec3 viewPos;
    viewPos.x = fpsCamera.getPosition().x;
    viewPos.y = fpsCamera.getPosition().y;
    viewPos.z = fpsCamera.getPosition().z;
    glm::vec3 CameraPosition(glm::inverse(view)[3]);
    glm::mat4 ViewProjectionMatrix = projection * view;
    //BEGIN PARTICLES
    int newparticles = (int)(deltaTime * 10000.0);
    if (newparticles > (int)(0.016f * 10000.0))
        newparticles = (int)(0.016f * 10000.0);

    for (int i = 0; i < newparticles; i++)
    {
        int particleIndex = FindUnusedParticle();
        ParticlesContainer[particleIndex].life = 1.0f; // This particle will live 5 seconds.
        ParticlesContainer[particleIndex].pos = glm::vec3(0, 0, -11.0f);

        float spread = 1.5f;
        glm::vec3 maindir = glm::vec3(0.0f, 10.0f, 0.0f);
        // Very bad way to generate a random direction;
        // See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead,
        // combined with some user-controlled parameters (main direction, spread, etc)
        glm::vec3 randomdir = glm::vec3(
            (rand() % 2000 - 1000.0f) / 1000.0f,
            (rand() % 2000 - 1000.0f) / 1000.0f,
            (rand() % 2000 - 1000.0f) / 1000.0f);

        ParticlesContainer[particleIndex].speed = maindir + randomdir * spread;

        // Very bad way to generate a random color
        ParticlesContainer[particleIndex].r = rand() % 256;
        ParticlesContainer[particleIndex].g = rand() % 256;
        ParticlesContainer[particleIndex].b = rand() % 256;
        ParticlesContainer[particleIndex].a = (rand() % 256) / 3;

        ParticlesContainer[particleIndex].size = (rand() % 1000) / 2000.0f + 0.1f;
    }
    // Simulate all particles
    int ParticlesCount = 0;
    for (int i = 0; i < MaxParticles; i++)
    {

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

        if (p.life > 0.0f)
        {

            // Decrease life
            p.life -= deltaTime;
            if (p.life > 0.0f)
            {

                // Simulate simple physics : gravity only, no collisions
                p.speed += glm::vec3(0.0f, -9.81f, 0.0f) * (float)deltaTime * 0.5f;
                p.pos += p.speed * (float)deltaTime;
                // if (i == 1)
                // {
                //  // std::cout << glm::to_string(p.pos) << std::endl;
                // }

                // std::cout << glm::to_string(p.pos) << std::endl;
                p.cameradistance = glm::length2(p.pos - CameraPosition);
                //ParticlesContainer[i].pos += glm::vec3(0.0f,10.0f, 0.0f) * (float)delta;

                // Fill the GPU buffer
                g_particule_position_size_data[4 * ParticlesCount + 0] = p.pos.x;
                g_particule_position_size_data[4 * ParticlesCount + 1] = p.pos.y;
                g_particule_position_size_data[4 * ParticlesCount + 2] = p.pos.z;

                g_particule_position_size_data[4 * ParticlesCount + 3] = p.size;

                g_particule_color_data[4 * ParticlesCount + 0] = p.r;
                g_particule_color_data[4 * ParticlesCount + 1] = p.g;
                g_particule_color_data[4 * ParticlesCount + 2] = p.b;
                g_particule_color_data[4 * ParticlesCount + 3] = p.a;
            }
            else
            {
                // Particles that just died will be put at the end of the buffer in SortParticles();
                p.cameradistance = -1.0f;
            }

            ParticlesCount++;
        }
    }
    SortParticles();
    glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
    glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
    glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLfloat) * 4, g_particule_position_size_data);

    glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
    glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
    glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLubyte) * 4, g_particule_color_data);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Use our shader
    glUseProgram(programID);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Texture);
    // Set our "myTextureSampler" sampler to use Texture Unit 0
    glUniform1i(TextureID, 0);
    glUniform3f(CameraRight_worldspace_ID, ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
    glUniform3f(CameraUp_worldspace_ID, ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);

    glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);

    // 1rst attribute buffer : vertices
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
    glVertexAttribPointer(
        0,        // attribute. No particular reason for 0, but must match the layout in the shader.
        3,        // size
        GL_FLOAT, // type
        GL_FALSE, // normalized?
        0,        // stride
        (void *)0 // array buffer offset
    );
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
    glVertexAttribPointer(
        1,        // attribute. No particular reason for 1, but must match the layout in the shader.
        4,        // size : x + y + z + size => 4
        GL_FLOAT, // type
        GL_FALSE, // normalized?
        0,        // stride
        (void *)0 // array buffer offset
    );

    // 3rd attribute buffer : particles' colors
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
    glVertexAttribPointer(
        2,                // attribute. No particular reason for 1, but must match the layout in the shader.
        4,                // size : r + g + b + a => 4
        GL_UNSIGNED_BYTE, // type
        GL_TRUE,          // normalized?    *** YES, this means that the unsigned char[4] will be accessible with a vec4 (floats) in the shader ***
        0,                // stride
        (void *)0         // array buffer offset
    );

    // These functions are specific to glDrawArrays*Instanced*.
    // The first parameter is the attribute buffer we're talking about.
    // The second parameter is the "rate at which generic vertex attributes advance when rendering multiple instances"
    // http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml
    glVertexAttribDivisor(0, 0); // particles vertices : always reuse the same 4 vertices -> 0
    glVertexAttribDivisor(1, 1); // positions : one per quad (its center)                 -> 1
    glVertexAttribDivisor(2, 1); // color : one per quad                                  -> 1
    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    //END OF PARTICLES
    // Must be called BEFORE setting uniforms because setting uniforms is done
    // on the currently active shader program.

    lightingShader.use();
    lightingShader.setUniform("model", glm::mat4(1.0)); // do not need to translate the models so just send the identity matrix
    lightingShader.setUniform("view", view);
    lightingShader.setUniform("projection", projection);
    lightingShader.setUniform("viewPos", viewPos);

    // // Directional light
    lightingShader.setUniform("sunLight.direction", glm::vec3(0.0f, -0.9f, -0.17f));
    lightingShader.setUniform("sunLight.ambient", glm::vec3(0.1f, 0.1f, 0.1f));
    lightingShader.setUniform("sunLight.diffuse", glm::vec3(0.1f, 0.1f, 0.1f)); // dark
    lightingShader.setUniform("sunLight.specular", glm::vec3(0.1f, 0.1f, 0.1f));


    lightingShader.setUniform("spotLight.ambient", glm::vec3(0.1f, 0.1f, 0.1f));
    lightingShader.setUniform("spotLight.diffuse", glm::vec3(0.8f, 0.8f, 0.8f));
    lightingShader.setUniform("spotLight.specular", glm::vec3(1.0f, 1.0f, 1.0f));
    lightingShader.setUniform("spotLight.position", glm::vec3(0.982347, 3.500000, 10.248156));
    lightingShader.setUniform("spotLight.direction", glm::vec3(-0.202902, -0.470038, -0.859008));
    lightingShader.setUniform("spotLight.cosInnerCone", glm::cos(glm::radians(15.0f)));
    lightingShader.setUniform("spotLight.cosOuterCone", glm::cos(glm::radians(20.0f)));
    lightingShader.setUniform("spotLight.constant", 1.0f);
    lightingShader.setUniform("spotLight.linear", 0.007f);
    lightingShader.setUniform("spotLight.exponent", 0.0017f);
    lightingShader.setUniform("spotLight.on", gFlashlightOn);

    // Render the scene
    for (int i = 0; i < 1; i++)
    {
        model = glm::translate(glm::mat4(1.0), modelPos[i]) * glm::scale(glm::mat4(1.0), modelScale[i]); // * glm::rotate(glm::mat4(1.0), glm::radians((float)(glfwGetTime() * 100.0f)), glm::vec3(1.0f, 0.0f, 0.0f));
        ;
        lightingShader.setUniform("model", model);

        //  // Set material properties
        lightingShader.setUniform("material.ambient", glm::vec3(0.1f, 0.1f, 0.1f));
        lightingShader.setUniformSampler("material.diffuseMap", 0);
        lightingShader.setUniform("material.specular", glm::vec3(0.8f, 0.8f, 0.8f));
        lightingShader.setUniform("material.shininess", 32.0f);

        texture[i].bind(0); // set the texture before drawing.  Our simple OBJ mesh loader does not do materials yet.
        mesh[i].draw();     // Render the OBJ mesh
        texture[i].unbind(0);
    }

    // Swap front and back buffers
    glfwSwapBuffers(gWindow);
    mac_patch(gWindow);
    lastTime = currentTime;
}

只有3D场景的渲染方式如下: enter image description here

当我注释掉网格逻辑的渲染时,即(本节)

for (int i = 0; i < 1; i++)
        {
            model = glm::translate(glm::mat4(1.0), modelPos[i]) * glm::scale(glm::mat4(1.0), modelScale[i]); // * glm::rotate(glm::mat4(1.0), glm::radians((float)(glfwGetTime() * 100.0f)), glm::vec3(1.0f, 0.0f, 0.0f));
            ;
            lightingShader.setUniform("model", model);

            //  // Set material properties
            lightingShader.setUniform("material.ambient", glm::vec3(0.1f, 0.1f, 0.1f));
            lightingShader.setUniformSampler("material.diffuseMap", 0);
            lightingShader.setUniform("material.specular", glm::vec3(0.8f, 0.8f, 0.8f));
            lightingShader.setUniform("material.shininess", 32.0f);

            texture[i].bind(0); // set the texture before drawing.  Our simple OBJ mesh loader does not do materials yet.
            mesh[i].draw();     // Render the OBJ mesh
            texture[i].unbind(0);
        }

我明白了:

enter image description here

我如何同时渲染它们?

我的代码库:github

1 个答案:

答案 0 :(得分:1)

在为粒子指定顶点属性数组之前,您只是错过了为粒子绑定顶点数组对象的情况:

 while (!glfwWindowShouldClose(gWindow))
 {
     // [...]

     glBindVertexArray(VertexArrayID); // <--- this is missing

     glEnableVertexAttribArray(0);
     glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
     glVertexAttribPointer(
         // [...]
     );

     // [...]

     glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);

     // [...]

 }

但是请注意,一次指定通用顶点属性数据的数组并绑定顶点数组对象以进行绘制就足够了:

glBindVertexArray(VertexArrayID);

// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
glVertexAttribPointer(
    // [...]
);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
glVertexAttribPointer(
    // [...]
);

// 3rd attribute buffer : particles' colors
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
glVertexAttribPointer(
    // [...]
);

glVertexAttribDivisor(0, 0); // particles vertices : always reuse the same 4 vertices -> 0
glVertexAttribDivisor(1, 1); // positions : one per quad (its center)                 -> 1
glVertexAttribDivisor(2, 1); // color : one per quad                                  -> 1

while (!glfwWindowShouldClose(gWindow))
{
     // [...]

     glBindVertexArray(VertexArrayID);
     glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);

     // [...]

     for (int i = 0; i < 1; i++)
     {
         // [...]

        texture[i].bind(0);
        mesh[i].draw();
        texture[i].unbind(0);
     }

     // [...]
}