简体   繁体   中英

Instantiation order changing draw in OpenGL using VAO

I trying to use VAOs, VBOs and IBOs to draw a bunch of sphere over a plane. Before using these, everything was drawn as expected. After I started to use those, things got weird. I can't post my whole code here because I have 5 classes (but if necessary I can provide a link to my code), so I'll try to post what I think it's useful.

With this class I can draw a sphere:

SphereShaderProgram::SphereShaderProgram(std::string vertexShaderPath, std::string fragmentShaderPath) : ProgramManager(vertexShaderPath, fragmentShaderPath)
{
    _sphereH = 20;
    _sphereW = 20;
    _vbo = 0;
    _vao = 0;
    _ibo = 0;
    CreateProgram();
    BuildSphere();
    BuildVAO();
}


SphereShaderProgram::~SphereShaderProgram()
{
    glDeleteVertexArrays(1, &_vao);
    glDeleteBuffers(1, &_vbo);
    glDeleteBuffers(1, &_ibo);
}


void SphereShaderProgram::DrawSphere(const glm::mat4 &Projection, const glm::mat4 &ModelView)
{
    _ModelViewProjection = Projection * ModelView;
    _ModelView = ModelView;

    Bind(); //glUseProgram

    glBindVertexArray(_vao);
    LoadVariables();
    glDrawElements(GL_TRIANGLES, _sphereIndexes.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    UnBind();
}


int SphereShaderProgram::Get1DIndex(int line, int column)
{
    return line * (int) _sphereH + column;
}


void SphereShaderProgram::BuildSphere()
{
    for (int l = 0; l < _sphereH - 1; l++)
    {
        for (int c = 0; c < _sphereW - 1; c++)
        {
            int v1_1 = Get1DIndex(l, c);
            int v2_1 = Get1DIndex(l + 1, c + 1);
            int v3_1 = Get1DIndex(l + 1, c);

            int v1_2 = Get1DIndex(l, c);
            int v2_2 = Get1DIndex(l, c + 1);
            int v3_2 = Get1DIndex(l + 1, c + 1);

            _sphereIndexes.push_back(v1_1);
            _sphereIndexes.push_back(v2_1);
            _sphereIndexes.push_back(v3_1);

            _sphereIndexes.push_back(v1_2);
            _sphereIndexes.push_back(v2_2);
            _sphereIndexes.push_back(v3_2);
        }
    }

    for (int l = 0; l < _sphereH; l++)
    {
        for (int c = 0; c < _sphereW; c++)
        {
            float theta = ((float) l / (_sphereH - 1)) * (float) PI;
            float phi = ((float) c / (_sphereW - 1)) * 2 * (float) PI;
            float x = sin(theta) * cos(phi);
            float z = sin(theta) * sin(phi);
            float y = cos(theta);

            _sphereCoordinates.push_back(x);
            _sphereCoordinates.push_back(y);
            _sphereCoordinates.push_back(z);
        }
    }
}


void SphereShaderProgram::BuildVAO()
{
    // Generate and bind the vertex array object
    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);

    // Generate and bind the vertex buffer object
    glGenBuffers(1, &_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);

    // Generate and bind the index buffer object
    glGenBuffers(1, &_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _sphereIndexes.size() * sizeof(unsigned int), &_sphereIndexes[0], GL_STATIC_DRAW);

    glBindVertexArray(0);
}


void SphereShaderProgram::LoadUniformVariables()
{
    glm::mat4 MVP = _ModelViewProjection;
    glm::mat4 MV = _ModelView;
    glm::mat3 N = glm::transpose(glm::inverse(glm::mat3(MV)));
    glm::vec4 AC = glm::vec4(0.2, 0.2, 0.2, 1.0);
    glm::vec4 DC = glm::vec4(0.7, 0.0, 0.0, 1.0);
    glm::vec4 SC = glm::vec4(0.1, 0.1, 0.1, 1.0);
    glm::vec3 LP = glm::vec3(1.0, 6.0, 4.0);

    // OpenGL Matrices 
    GLuint ModelViewProjection_location = glGetUniformLocation(GetProgramID(), "mvpMatrix");
    glUniformMatrix4fv(ModelViewProjection_location, 1, GL_FALSE, glm::value_ptr(MVP));

    GLuint ModelView_location = glGetUniformLocation(GetProgramID(), "mvMatrix");
    glUniformMatrix4fv(ModelView_location, 1, GL_FALSE, glm::value_ptr(MV));

    GLuint Normal_location = glGetUniformLocation(GetProgramID(), "normalMatrix");
    glUniformMatrix3fv(Normal_location, 1, GL_FALSE, glm::value_ptr(N));

    // Lighting 
    GLuint AmbientColor_location = glGetUniformLocation(GetProgramID(), "ambientColor");
    glUniform4fv(AmbientColor_location, 1, glm::value_ptr(AC));

    GLuint DiffuseColor_location = glGetUniformLocation(GetProgramID(), "diffuseColor");
    glUniform4fv(DiffuseColor_location, 1, glm::value_ptr(DC));

    GLuint SpecularColor_location = glGetUniformLocation(GetProgramID(), "specularColor");
    glUniform4fv(SpecularColor_location, 1, glm::value_ptr(SC));

    GLuint LightPosition_location = glGetUniformLocation(GetProgramID(), "vLightPosition");
    glUniform3fv(LightPosition_location, 1, glm::value_ptr(LP));
}


void SphereShaderProgram::LoadAtributeVariables()
{
    // Vertex Attributes
    GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
    glEnableVertexAttribArray(VertexPosition_location);

    glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
}


void SphereShaderProgram::LoadVariables()
{
    LoadUniformVariables();
    LoadAtributeVariables();
}

And with that, a plane:

PlaneShaderProgram::PlaneShaderProgram(std::string vertexShaderPath, std::string fragmentShaderPath) : ProgramManager(vertexShaderPath, fragmentShaderPath)
{
    CreateProgram();
    _vbo = 0;
    _vao = 0;
    _ibo = 0;
    BuildPlane();
    BuildVAO();
}


PlaneShaderProgram::~PlaneShaderProgram()
{
    glDeleteVertexArrays(1, &_vao);
    glDeleteBuffers(1, &_vbo);
    glDeleteBuffers(1, &_ibo);
}


void PlaneShaderProgram::DrawPlane(const glm::mat4 &Projection, const glm::mat4 &ModelView)
{
    _ModelViewProjection = Projection * ModelView;
    _ModelView = ModelView;

    Bind();

    glBindVertexArray(_vao);
    LoadVariables();
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    UnBind();
}


void PlaneShaderProgram::BuildPlane()
{
    _coordinates[0]  = -1.0f;
    _coordinates[1]  =  0.0f;
    _coordinates[2]  = -1.0f;
    _coordinates[3]  = -1.0f;
    _coordinates[4]  =  0.0f;
    _coordinates[5]  =  1.0f;
    _coordinates[6]  =  1.0f;
    _coordinates[7]  =  0.0f;
    _coordinates[8]  =  1.0f;
    _coordinates[9]  =  1.0f;
    _coordinates[10] =  0.0f; 
    _coordinates[11] = -1.0f;

    _indexes[0] = 0;
    _indexes[1] = 1;
    _indexes[2] = 2;
    _indexes[3] = 0;
    _indexes[4] = 2;
    _indexes[5] = 3;
}


void PlaneShaderProgram::BuildVAO()
{
    // Generate and bind the vertex array object
    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);

    // Generate and bind the vertex buffer object
    glGenBuffers(1, &_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), _coordinates, GL_STATIC_DRAW);

    // Generate and bind the index buffer object
    glGenBuffers(1, &_ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLuint), _indexes, GL_STATIC_DRAW);

    glBindVertexArray(0);
}


void PlaneShaderProgram::LoadUniformVariables()
{
    // OpenGL Matrices
    GLuint ModelViewProjection_location = glGetUniformLocation(GetProgramID(), "mvpMatrix");
    glUniformMatrix4fv(ModelViewProjection_location, 1, GL_FALSE, glm::value_ptr(_ModelViewProjection));
}


void PlaneShaderProgram::LoadAtributeVariables()
{
    // Vertex Attributes
    GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
    glEnableVertexAttribArray(VertexPosition_location);

    glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
}


void PlaneShaderProgram::LoadVariables()
{
    LoadUniformVariables();
    LoadAtributeVariables();
}

This, on the other hand, is my main:

int main(void)
{
    // Set the error callback  
    glfwSetErrorCallback(ErrorCallback);

    // Initialize GLFW  
    if (!glfwInit())
    {
        printf("Error initializing GLFW!\n");
        exit(EXIT_FAILURE);
    }

    // Set the GLFW window creation hints - these are optional  
    glfwWindowHint(GLFW_SAMPLES, 4);

    // Create a window and create its OpenGL context    
    GLFWwindow* window = glfwCreateWindow(width, height, "OpenGL 4 Base", NULL, NULL);

    // If the window couldn't be created  
    if (!window)
    {
        fprintf(stderr, "Failed to open GLFW window.\n");
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    // Sets the context of the specified window on the calling thread  
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    glewExperimental = true;
    GLenum glewError = glewInit();
    if (glewError != GLEW_OK)
    {
        printf("Error initializing GLEW! %s\n", glewGetErrorString(glewError));
        glfwDestroyWindow(window);
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(window, KeyCallback);
    glfwSetWindowSizeCallback(window, WindowSizeCallback);
    glfwSetScrollCallback(window, ScrollCallback);

    // Set the view matrix
    glm::mat4 ModelView = glm::lookAt(glm::vec3(0.0f, 7.0f, 15.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

    // Init matrix stack
    glm_ModelViewMatrix.push(ModelView);

    PlaneShaderProgram PlaneShaderProgram("FloorVertexShader.txt", "FloorFragShader.txt");
    SphereShaderProgram SphereShaderProgram("ADSPerVertexVertexShader.txt", "ADSPerVertexFragShader.txt");
    //SphereShaderProgram SphereShaderProgram = SphereShaderProgram("ADSPerPixelVertexShader.txt", "ADSPerPixelFragShader.txt");

    // Set a background color  
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    // 3D objects
    glEnable(GL_DEPTH_TEST);

    float d = 2.0f;
    float p0 = -10.0f + d / 2;
    // Main Loop  
    while (!glfwWindowShouldClose(window))
    {
        // Clear color buffer  
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Clone current modelview matrix, which can now be modified 
        glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
        {
            //------- ModelView Transformations
            // Zoom in/out
            glm_ModelViewMatrix.top() = glm::translate(glm_ModelViewMatrix.top(), glm::vec3(0.0, 0.0, zoom));
            // Rotation
            glm_ModelViewMatrix.top() = glm::rotate(glm_ModelViewMatrix.top(), beta, glm::vec3(1.0, 0.0, 0.0));
            glm_ModelViewMatrix.top() = glm::rotate(glm_ModelViewMatrix.top(), alpha, glm::vec3(0.0, 0.0, 1.0));

            //------- Draw the plane
            glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
            {
                glm_ModelViewMatrix.top() = glm::scale(glm_ModelViewMatrix.top(), glm::vec3(7.0f, 1.0f, 7.0f));

                PlaneShaderProgram.DrawPlane(Projection, glm_ModelViewMatrix.top());
            }
            glm_ModelViewMatrix.pop();

            //------- Draw spheres
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
                    {
                        glm_ModelViewMatrix.top() = glm::scale(glm_ModelViewMatrix.top(), glm::vec3(0.5f, 0.5f, 0.5f));
                        glm_ModelViewMatrix.top() = glm::translate(glm_ModelViewMatrix.top(), glm::vec3(p0 + i * d, 1.0f, p0 + j * d));

                        SphereShaderProgram.DrawSphere(Projection, glm_ModelViewMatrix.top());
                    }
                    glm_ModelViewMatrix.pop();
                }
            }
        }
        glm_ModelViewMatrix.pop();

        // Swap buffers  
        glfwSwapBuffers(window);

        // Get and organize events, like keyboard and mouse input, window resizing, etc...  
        glfwPollEvents();
    }

    // Close OpenGL window and terminate GLFW  
    glfwDestroyWindow(window);
    // Finalize and clean up GLFW  
    glfwTerminate();

    exit(EXIT_SUCCESS);
}

Instantiating the plane and then the sphere program, I get the following result (no plane at all):

在此处输入图片说明

Changing the order, that is the result:

在此处输入图片说明

I'm trying to find a clue about what I'm missing, because I don't have any idea about what is wrong. Before using VAOs (just using glVertexAttribPointer and glDrawElements ), everything was drawn correctly.

Thank you in advance.

The problem is with the placement of the glVertexAttribPointer() call. You're calling it in the LoadAtributeVariables() method, which in turn is called from the Draw*() method.

This should really be part of the VAO setup, for a couple of reasons:

  • It's inefficient to make the call on every redraw. This call sets up state that is part of the VAO state. That's the whole idea of using VAOs in the first place. You can set up all this state once during setup, and then only need to bind the VAO again before the draw call, which sets up all the state again with a single call.
  • In your case, the VBO is not bound at the time you make the call. glVertexAttribPointer() sets up the attribute to pull data from the currently bound VBO, ie the buffer bound as GL_ARRAY_BUFFER .

The first problem is only a performance issue. The second is the reason why your code does not work, since you do not have the correct VBO bound when glVertexAttribPointer() is called.

To fix this, you only need to move the LoadAtributeVariables() call into BuildVAO() , at this location:

// Generate and bind the vertex buffer object
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);

LoadAtributeVariables();

and remove it from where it currently is, so that it is not called before each draw call anymore.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM