Thums
Thums

Reputation: 187

Shape not being drawn in OpenGL when unbinding vertex array

I'm learning OpenGL and I set up a Mesh class to render my shapes. Drawing only works without

glBindVertexArray(0);

at the end so I'm guessing I messed up something but I can't figure it out.

Here is the code: main.cpp

#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <iostream>
#include "mesh.h"

#define WIDTH 1280
#define HEIGHT 720

// Vertex shader
    const char* vs = "\n\
    #version 330                \n\
    layout (location = 0) in vec2 position; \n\
    layout (location = 1) in vec3 color;    \n\
    out vec3 Color;             \n\
                        \n\
    void main() {               \n\
        Color = color;          \n\
        gl_Position = vec4(position, 0.0, 1.0); \n\
    }                   \n\
                        \n\
    ";
// Fragment shader
    const char* fs = "\n\
    #version 330                \n\
    in vec3 Color;              \n\
    out vec4 fragColor;             \n\
                        \n\
    void main() {               \n\
    fragColor = vec4(Color, 1.0);       \n\
    }                   \n\
    ";

bool checkShader(GLuint shader) {
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

    if (status != GL_TRUE) {
        char buffer[512];
        glGetShaderInfoLog(shader, 512, NULL, buffer);
        std::cout << "Error compiling shader:" << buffer << std::endl;
        return false;
    }
    return true;
}

bool compileShaders() {
    // compile the vertex shader
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vs, NULL);
    glCompileShader(vertexShader);

    if (!checkShader(vertexShader))
        return false;

    // compile the fragment shader
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fs, NULL);
    glCompileShader(fragmentShader);

    if (!checkShader(fragmentShader))
        return false;

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);

    glBindFragDataLocation(shaderProgram, 0, "outColor");

    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);

    return true;
}

int main() {
    bool success = true;
    bool running = true;

    SDL_Window* window;
    SDL_GLContext context;

    // Initialize SDL/Glew

    if (SDL_Init(SDL_INIT_VIDEO) <  0) {
        std::cerr << "Video initialization failed:" << SDL_GetError() << std::endl;
        success = false;
    }

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    window = SDL_CreateWindow("Game Title", SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN |
        SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS);
    context = SDL_GL_CreateContext(window);

    glewExperimental = GL_TRUE; 
    if (glewInit() != GLEW_OK) {
        std::cerr << "Problem initializing GLEW." << std::endl;
        success = false;
    }

    SDL_Event e;

    // Mesh setup
    float vertices[] = { 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
                 0.5f,-0.5f, 0.0f, 1.0f, 0.0f,
                -0.5f,-0.5f, 0.0f, 0.0f, 1.0f };

    float vertices2[] = { -1.0f,-1.0f, 1.0f, 0.0f, 0.0f,
                  -1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
                  -0.0f, 0.0f, 0.0f, 0.0f, 1.0f };

    GLuint elements[] = { 0, 1, 2 };

    Mesh triangle(vertices, elements, sizeof(vertices), sizeof(elements));
    Mesh triangle2(vertices2, elements, sizeof(vertices2), sizeof(elements));

    if (!compileShaders())
        success = false;

    if (!success) {
        std::cerr << "A problem ocurred during initialization, will exit." << std::endl;
        exit(1);
    }

    while (running) {
        while (SDL_PollEvent(&e)) {
            if (e.type ==  SDL_QUIT)
                running = false;
            if (e.type == SDL_KEYDOWN)
                if (e.key.keysym.sym == SDLK_ESCAPE)
                    running = false;
        }

        glClearColor(0.0f, 1.0f, 0.5f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        triangle.draw();
        triangle2.draw();

        SDL_GL_SwapWindow(window);
        SDL_Delay(1);
    }

    SDL_GL_DeleteContext(context);
    SDL_Quit();
    return 0;
}

mesh.h

#ifndef MESH_H
#define MESH_H
#include <GL/glew.h>

class Mesh {
private:
    GLuint IBO;
    GLuint VBO;
    GLuint VAO;
    float* vertices;
    GLuint* indices;

    int sizeVertices;
    int sizeIndices;
public:
    Mesh(float* vertices, GLuint* indices, int sizeVertices, int sizeIndices);
    void draw();
};

#endif //MESH_H

mesh.cpp

#include "mesh.h"

#define POSITION 0
#define COLOR 1

Mesh::Mesh(float* vertices, GLuint* indices, int sizeVertices, int sizeIndices) {
    this->vertices = vertices;
    this->indices = indices;
    this->sizeVertices = sizeVertices;
    this->sizeIndices = sizeIndices;

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &IBO);
}

void Mesh::draw() {
    glEnableVertexAttribArray(POSITION);
    glVertexAttribPointer(POSITION, 2, GL_FLOAT, GL_FALSE,5*sizeof(float), 0);

    glEnableVertexAttribArray(COLOR);
    glVertexAttribPointer(COLOR, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float)));

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeVertices, vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeIndices, indices, GL_STATIC_DRAW);

    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

    //glBindVertexArray(0); If I uncomment this, the mesh is not displayed
}

Upvotes: 0

Views: 318

Answers (1)

datenwolf
datenwolf

Reputation: 162299

The calls to glVertexAttribPointer pointer refer to the VAO and VBO bound at the time of calling. In your code you're calling glVertexAttribPointer before the VAO and the VBO are bound. So the first iteration round those calls will fail. Without unbinding the VAO/VBO the next iteration the VAO/VBO state will happen (by circumstance) to the more or less right; actually you've swapped the VAO/VBO between the two triangle instances.

Update – fixed code

Mesh::Mesh(float* vertices, GLuint* indices, int sizeVertices, int sizeIndices) {
    this->vertices = vertices;
    this->indices = indices;
    this->sizeVertices = sizeVertices;
    this->sizeIndices = sizeIndices;

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &IBO);

    /* VAOs contain ARRAY_BUFFERS */
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeVertices, vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(POSITION);
    glVertexAttribPointer(POSITION, 2, GL_FLOAT, GL_FALSE,5*sizeof(float), 0);

    glEnableVertexAttribArray(COLOR);
    glVertexAttribPointer(COLOR, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float)));

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(VAO);

    /* ELEMENT_ARRAY_BUFFERS are not contained in VAOs */
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeIndices, indices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void Mesh::draw() {
    /* VAOs keep the glVertexAttribPointer bindings,
     * so it's sufficient to just bind the VAO;
     * no need for binding the VBOs (as long as you don't
       want to change the pointers). */
    glBindVertexArray(VAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

    /* Need to bind the ELEMENT_ARRAY_BUFFER though */

    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

Upvotes: 2

Related Questions