2

im learning about OpenGL and i wrote the following code in C++ using this guide and this video. Also i am using GLFW for context creation and GLEW for GL functions Most of the Shader class is copied from the video up linked,

The problem is that using glDrawElements() to render inside the main loop gets me a segmentation fault:

Segmentation fault


------------------
(program exited with code: 139)
Press return to continue

while with glDrawArrays() i can draw with no problems.

Does anyone know what this could be caused by? I think the error might depend on the implementation of the Shader class, because i used glDrawArrays() in other programs that did not used this class and that cared about shaders in the main function.

program.cpp

//INCLUDE AND DECLARATIONS
#include <iostream>
#include <fstream> 
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include "Shader.h"

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
unsigned long getFileLength(std::ifstream& file);
int loadshader(char* filename, GLchar** ShaderSource, unsigned long* len);

const GLuint WIDTH = 800, HEIGHT = 600;


//VERTEX DATA
float data[] = {
//    X      Y     R     G     B   
    -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, // Top-left
     0.5f,  0.5f, 0.0f, 1.0f, 0.0f, // Top-right
     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right
    -0.5f, -0.5f, 1.0f, 1.0f, 1.0f  // Bottom-left
};

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

//main
int main()
{   //INIT GLFW AND WINDOW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glewExperimental = GL_TRUE;
    glewInit();
    glViewport(0, 0, WIDTH, HEIGHT);



    //ALLOCATE BUFFERS
        //VERTEX ARRAY BUFFER
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
        //ELEMENT ARRAY BUFFER
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
        //CREATE SHADER
    Shader shader("./shaders/basicShader");


    // main loop
    while (!glfwWindowShouldClose(window))
    {
        shader.Bind();
        glfwPollEvents();                       //window events
        glClearColor(1.0f, 0.0f, 0.5f, 0.5f);   //background
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);                //update window
    }

    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);
    glfwTerminate();
    return 0;
}


void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

Shader.h

#include <iostream>
#include <fstream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>


class Shader
{
        public:
            Shader(const std::string& filepath);
            ~Shader();
            void Bind();
        private:
            static const GLuint NUM_SHADERS = 2;
            GLuint program;  
            GLuint shaders[NUM_SHADERS];

            std::string LoadShader(const std::string& fileName);
            void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage);
            GLuint CreateShader(const std::string& text, unsigned int type);

};

Shader.cpp

#include "Shader.h"

Shader::Shader(const std::string& filepath)
{
    program = glCreateProgram();
    shaders[0] = CreateShader(LoadShader(filepath + ".vs"), GL_VERTEX_SHADER);
    shaders[1] = CreateShader(LoadShader(filepath + ".fs"), GL_FRAGMENT_SHADER);

    for(unsigned int i = 0; i < NUM_SHADERS; i++)
    {
        glAttachShader(program, shaders[i]);
    }
    glBindAttribLocation(program, 0, "position");
    glBindFragDataLocation(program, 0, "outColor");

    glLinkProgram(program);
    CheckShaderError(program, GL_LINK_STATUS, true, "Error linking shader program");

    glValidateProgram(program);
    CheckShaderError(program, GL_LINK_STATUS, true, "Invalid shader program");




    GLuint vao;
    glGenVertexArrays(1, &vao);     
    glBindVertexArray(vao);     

    GLint posAttrib = glGetAttribLocation(program, "position");
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
    glEnableVertexAttribArray(posAttrib);

    GLint AttribColor = glGetAttribLocation(program, "color");
    glVertexAttribPointer(AttribColor, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float)));
    glEnableVertexAttribArray(AttribColor);


}

Shader::~Shader()
{
    for(unsigned int i = 0; i < NUM_SHADERS; i++)
    {
        glDetachShader(program, shaders[i]);
        glDeleteShader(shaders[i]);
    }
    glDeleteProgram(program);
}

void Shader::Bind()
{
        glUseProgram(program);
}


//loads shaders from files
std::string Shader::LoadShader(const std::string& fileName)
{
    std::ifstream file;
    file.open((fileName).c_str());

    std::string output;
    std::string line;

    if(file.is_open())
    {
        while(file.good())
        {
            getline(file, line);
            output.append(line + "\n");
        }
    }
    else
    {
        std::cerr << "Unable to load shader: " << fileName << std::endl;
    }

    return output;
}
//Checks for eventual errors in shaders
void Shader::CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage)
{
    GLint success = 0;
    GLchar error[1024] = { 0 };

    if(isProgram)
        glGetProgramiv(shader, flag, &success);
    else
        glGetShaderiv(shader, flag, &success);

    if(success == GL_FALSE)
    {
        if(isProgram)
            glGetProgramInfoLog(shader, sizeof(error), NULL, error);
        else
            glGetShaderInfoLog(shader, sizeof(error), NULL, error);

        std::cerr << errorMessage << ": '" << error << "'" << std::endl;
    }
}

GLuint Shader::CreateShader(const std::string& text, unsigned int type)
{
    GLuint shader = glCreateShader(type);
        if(shader == 0)
            std::cerr << "error allocating shader" << std:: endl;

    const GLchar* p[1];
    p[0] = text.c_str();
    GLint lengths[1];
    lengths[0] = text.length();

    glShaderSource(shader, 1, p, lengths);
    glCompileShader(shader);
    CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error compiling shader!");

    return shader;
}
  • last argument to ``glDrawElements()`` is a pointer to the index array. You give 0. Might that be the problem? – BitTickler Sep 04 '15 at 23:02

1 Answers1

7

The problem is with your index buffer binding. The index buffer (GL_ELEMENT_ARRAY_BUFFER) binding is part of the VAO state. Skipping the unrelated calls, you have the following overall sequence:

...
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
...
GLuint vao;
glGenVertexArrays(1, &vao);     
glBindVertexArray(vao);     
...
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

Since the GL_ELEMENT_ARRAY_BUFFER binding is part of the VAO state, the current index buffer binding is replaced with the index buffer binding in the VAO state when you call:

glBindVertexArray(vao);     

Since you never bind an index buffer while the VAO is bound, this means that the index buffer binding in the VAO state is 0. As a result, you don't have an index buffer bound after this call.

What happens next is that you make the glDrawElements() call with the last argument 0. Without an index buffer bound, the last argument is interpreted as a pointer to CPU memory. So OpenGL tries to read index data at address 0, which causes the crash.

To fix this, you simply have to bind the index buffer while the VAO is bound. You can either do that by changing the order of your calls, and create/bind the VAO before you set up the index buffer. Or you can bind it again when you set up the vertex attribute state. For example, at the very end of the Shader constructor, after the glVertexAttribPointer() and related calls, you add this call:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133