Search code examples
shadervertex-bufferparticle-systemtransform-feedback

Render particles


I am trying to create a particle system. I am using transform feedback and at this moment I am just trying to get this to work for one point. The problem is that more than one point is rendered for every iteration of the draw loop. It seems that points keeps getting added despite the fact that my particle buffer only have room for one single point. Instead of drawing one point which is then moved, the loop adds one point for every iteration. Below is the code for the program and then the code for my vertex shader. At this moment I do not use any geometry shader.

    #include "stdafx.h"

    #include <stdio.h>
    #include <stdlib.h>

    #include "Dependencies/glew/glew.h"
    #include "Dependencies/glut/glut.h"

    #include <time.h>
    #include <iostream>
    #include <sys/stat.h>

    #include "Dependencies/glm/glm.hpp"
    using namespace glm;

    struct Particles{
         vec3 Position = vec3(0.0, 0.0, 0.0);
        //vec3 Velocity = vec3(0.0, 0.0, 0.0);
    };

    GLint inputAttrib;

    GLuint program, programFrag, drawProgram;                               //Program object for using shaders above
    GLuint m_TFB[2], m_PB[2];       //two transfromfbs and 2    pbuffers
    GLuint query;               //Keep track of amount of objects

    unsigned int m_currVB;
    unsigned int m_currTFB;

    bool first = true;

    const static int MAX_NUMBER = 1;

    char* readShaderFile(const char *filename) {
    FILE *file;
    struct stat st;

    file = fopen(filename, "r");

    if (file == NULL){
        fprintf(stderr, "ERROR: Cannot open shader file!");
        return 0;
    }

    stat(filename, &st);

    int bytesinfile = st.st_size;
    char *buffer = (char*)malloc(bytesinfile + sizeof(char));
    int bytesread = fread(buffer, 1, bytesinfile, file);
    buffer[bytesread] = 0; // Terminate the string with 0
    fclose(file);

    return buffer;
}

    void loadShaders(){
    //load our vertex shader
    GLint vertShader = glCreateShader(GL_VERTEX_SHADER);
    const char *vertexAssembly = readShaderFile("vertex_shader.vert");
    glShaderSource(vertShader, 1, &vertexAssembly, NULL);
    glCompileShader(vertShader);
    free((void *)vertexAssembly);

    GLint isCompiled;
    glGetShaderiv(vertShader, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        char str[256];
        glGetShaderInfoLog(vertShader, 256, NULL, str);
        fprintf(stderr, "Vertex shader compile error: %s\n", str);
    }

    program = glCreateProgram();
    glAttachShader(program, vertShader);

    const GLchar* feedbackVaryings[1];
    feedbackVaryings[0] = "Position0";
    //feedbackVaryings[1] = "Velocity0";
    glTransformFeedbackVaryings(program, 1, feedbackVaryings,          GL_INTERLEAVED_ATTRIBS);


    glLinkProgram(program);

    //load our vertex draw shader
    GLint vertDraw = glCreateShader(GL_VERTEX_SHADER);
    const char *drawVertAssembly = readShaderFile("draw.vert");
    glShaderSource(vertDraw, 1, &drawVertAssembly, NULL);
    glCompileShader(vertDraw);
    free((void *)drawVertAssembly);

    glGetShaderiv(vertDraw, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        char str[256];
        glGetShaderInfoLog(vertDraw, 256, NULL, str);
        fprintf(stderr, "Vertex draw shader compile error: %s\n", str);
    }
    else{
        std::cout << "Vert draw ok!" << std::endl;
    }

    drawProgram = glCreateProgram();
    glAttachShader(drawProgram, vertDraw);

    //load our vertex shader
    GLint fragDraw = glCreateShader(GL_FRAGMENT_SHADER);
    const char *drawFragAssembly = readShaderFile("draw.frag");
    glShaderSource(fragDraw, 1, &drawFragAssembly, NULL);
    glCompileShader(fragDraw);
    free((void *)drawFragAssembly);

    glGetShaderiv(fragDraw, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        char str[256];
        glGetShaderInfoLog(fragDraw, 256, NULL, str);
        fprintf(stderr, "Fragment draw shader compile error: %s\n", str);
    }
    else{
        std::cout << "Frag draw ok!" << std::endl;
    }

    glAttachShader(drawProgram, fragDraw);

    glLinkProgram(drawProgram);
}


    void initSystem(){

    loadShaders();

    Particles data[MAX_NUMBER]; 
    data[0].Position = vec3(0.0, 0.0, 0.0);
    //data[0].Velocity = vec3(0.1, 0.0, 0.0);

    glGenTransformFeedbacks(2, m_TFB);
    glGenBuffers(2, m_PB);
    for (int i = 0; i < 2; i++){
        glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TFB[i]);
        glBindBuffer(GL_ARRAY_BUFFER, m_PB[i]);
        //glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GL_FLOAT), data, GL_STREAM_DRAW);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vec3), &data[0], GL_DYNAMIC_DRAW);
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_PB[i]);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

}

    void updateParticles(){
    glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    //Don't draw anything yet
    glEnable(GL_RASTERIZER_DISCARD);

    //Bind particle buffer to write from
    glBindBuffer(GL_ARRAY_BUFFER, m_PB[m_currVB]);


    //Create our attribute pointer. We only have position in the loop. The array is tightly packed maybe? We want to start drawing from the beginning?
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Particles), (GLvoid*)0);
    //glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (const GLvoid*)(3 * sizeof(GL_FLOAT)));

    glEnableVertexAttribArray(0);
    //glEnableVertexAttribArray(1);

    //Bind transform buffer to write to
    //glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
    glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TFB[m_currTFB]);
    //Begin transformfeedback here we go man!!

    //Use our shaders for updating
    glUseProgram(program);

    glBeginTransformFeedback(GL_POINTS);

    glPointSize(20.0f);
    if (first){
        glDrawArrays(GL_POINTS, 0, 1);
        first = false;
    }
    else{
        glDrawTransformFeedback(GL_POINTS, m_TFB[m_currVB]);
     }

    glEndTransformFeedback();

    glUseProgram(0);

     //Disable dose arrays
     glDisableVertexAttribArray(0);
    //glDisableVertexAttribArray(1);

    //Unbind our buffer to end this part of the operation
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);


    }

    void drawParticles(){
    //Now we want to actually draw some shit on the screen
    glDisable(GL_RASTERIZER_DISCARD);

    //Use our program for rendering
    glUseProgram(drawProgram);

    glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


    //Bind particle buffer to draw points retrived from this draw call
    glBindBuffer(GL_ARRAY_BUFFER, m_PB[m_currTFB]);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Particles), (GLvoid*)0);

    glEnableVertexAttribArray(0);

    //Draw as many points as we have got in this draw call
    glDrawTransformFeedback(GL_POINTS, m_TFB[m_currVB]);

    glDisableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(0);

    glutSwapBuffers();

}

    void render(){

    updateParticles();

    drawParticles();

    /*unsigned int tmp;
    tmp = m_currTFB;
    m_currTFB = m_currVB;
    m_currVB = tmp;*/
    m_currVB = m_currTFB;
    m_currTFB = (m_currTFB + 1) & 0x1;

}


    void init(){
     glEnable(GL_DEPTH_TEST);

    srand(time(NULL));
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);

     glutInitWindowSize(600, 600);
    glutCreateWindow("ShaderTest");
     glutInitWindowPosition(100, 100);
    printf("%s\n", glGetString(GL_VERSION));
    glewInit();
    if (glewIsSupported("GL_VERSION_4_3"))
    {
        std::cout << " GLEW Version is 4.3\n ";
    }
    else
    {
        std::cout << "GLEW 4.3 not supported\n ";
    }

}

    int main(int argc, char **argv)
    {
    glutInit(&argc, argv);

    init();
    initSystem();
    glutDisplayFunc(render);
    glutIdleFunc(idle);
    glutReshapeFunc(reshape);

    glutMainLoop();
}




Below is the code for my vertex shader


    #version 330


    layout (location = 0) in vec3 Position;


    layout (location = 0) out vec3 Position0;

    void main() {
    Position0 = Position + vec3(0.1,0.1,0.0);// + Velocity;
    //Velocity0 =  Velocity;

    }

Furthermore I use a separate vertex shader and fragment shader for rendering the points.

    #version 330

    layout (location = 0) in vec3 Position;
    out vec4 gl_Position;
    void main() {
    gl_Position.xyz = Position;
    gl_Position.w = 1;
    }

    #version 330

    out vec4 frag_color;

    void main() {
        frag_color = vec4(1,0,0,1);
    }

Please let me know if anything is unclear and thanks in advance.


Solution

  • I managed to solve it myself. The line "glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT);" was wrong and should be "glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);".