Question

I'm following a tutorial to set up some VBOs for the modern OpenGL. I managed to get a mesh and shader running (shader set to make each pixel red).

Next tutorial I was to set up the shader to use texture mapping, which seems to load correctly in the debugger, but I'm not so sure since I get an error once I get to glDrawArrays(...);

I think this stuff is kind of hard to get into, so I'm guessing I'm probably doing something horribly wrong somewhere. Sorry in advance for what you might see x)

Error output:

First-chance exception at 0x52E4A415 (nvoglv32.dll) in Modern OpenGL.exe: 0xC0000005: Access violation reading location 0x00000000.

This is my mesh code (or VBO code):

Mesh::Mesh() {
    //Generate vertex array
    glGenVertexArrays(1, &arrayObject);

    for (int i = 0; i < VBO_COUNT; i++) {
        //Generate vertex buffer
        glGenBuffers(1, &buffers[i]);
    }
}

Mesh::Mesh(ObjectData *obj) {
    //Initialize first
    Mesh::Mesh();

    //Set object to parameter
    setObject(obj);
}

Mesh::~Mesh() {
    for (int i = 0; i < VBO_COUNT; i++) {
        //Delete buffer
        glDeleteBuffers(1, &buffers[i]);
    }

    //Delete array
    glDeleteVertexArrays(1, &arrayObject);
}

void Mesh::draw() {
    //Tell OpenGL which array to use
    glBindVertexArray(arrayObject);

    glDrawArrays(GL_TRIANGLES, 0, object->vertices.size());

    glBindVertexArray(NULL);
}

void Mesh::updateVBO() {
    //Tell OpenGL which vertex array to use from now
    glBindVertexArray(arrayObject);

    //Set buffer data
    glBindBuffer(GL_ARRAY_BUFFER, buffers[VBO_VERTEX]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(object->vertices[0]) * object->vertices.size(), &object->vertices.front(), GL_STATIC_DRAW);

    //Set shader attribute data
    glEnableVertexAttribArray(VBO_VERTEX);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    //Set buffer data
    glBindBuffer(GL_ARRAY_BUFFER, buffers[VBO_TEXCORD]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(object->texCoords[0]) * object->texCoords.size(), &object->texCoords.front(), GL_STATIC_DRAW);

    //Set shader attribute data
    glEnableVertexAttribArray(VBO_TEXCORD);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

    //Unbind vertex array
    glBindVertexArray(NULL);
}

void Mesh::setObject(ObjectData *obj) {
    object = obj;
    updateVBO();
}

Here is my shader code:

Shader::Shader(string fileName) {
    m_program = glCreateProgram();
    m_shaders[SHA_VERTEX] = createShader(loadShader(fileName + ".vs"), GL_VERTEX_SHADER);
    m_shaders[SHA_FRAGMENT] = createShader(loadShader(fileName + ".fs"), GL_FRAGMENT_SHADER);

    for (int i = 0; i < SHA_COUNT; i++) {
        glAttachShader(m_program, m_shaders[i]);
    }

    glBindAttribLocation(m_program, VBO_VERTEX, "vertices");
    glBindAttribLocation(m_program, VBO_TEXCORD, "texCoords");

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

    glValidateProgram(m_program);
    checkShaderError(m_program, GL_VALIDATE_STATUS, true, "Invalid shader program");
}

Shader::~Shader() {
    for (int i = 0; i < SHA_COUNT; i++) {
        glDetachShader(m_program, m_shaders[i]);
        glDeleteShader(m_shaders[i]);
    }

    glDeleteProgram(m_program);
}

string Shader::loadShader(string filePath) {
    ifstream file;
    file.open((filePath).c_str());

    string output;
    string line;

    if(file.is_open()) {
        while(file.good()) {
            getline(file, line);
            output.append(line + "\n");
        }
    }
    else {
        printf("Unable to load shader: %s\n", filePath.c_str());
    }

    return output;
}

void Shader::checkShaderError(GLuint shader, GLuint flag, bool isProgram, 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);
        }

        printf("%s: '%s'\n", errorMessage.c_str(), error);
    }
}

GLuint Shader::createShader(string text, unsigned int type) {
    GLuint shader = glCreateShader(type);
    if (shader == 0) {
        printf("Error compiling shader type %i\n", type);
    }

    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;
}

void Shader::bind() {
    glUseProgram(m_program);
}

EDIT: Here is my vertices and texCoords:

//Create test objects
ObjectData *obj = new ObjectData();

obj->vertices.push_back(glm::vec3(-1, 0, 0));
obj->vertices.push_back(glm::vec3(0, 0.5, 0));
obj->vertices.push_back(glm::vec3(0, -0.5, 0));
obj->texCoords.push_back(glm::vec2(0.0, 0.5));
obj->texCoords.push_back(glm::vec2(0.5, 0.75));
obj->texCoords.push_back(glm::vec2(0.5, 0.25));

obj->vertices.push_back(glm::vec3(1, 0, 0));
obj->vertices.push_back(glm::vec3(0, 0.5, 0));
obj->vertices.push_back(glm::vec3(0, -0.5, 0));
obj->texCoords.push_back(glm::vec2(1.0, 0.5));
obj->texCoords.push_back(glm::vec2(0.5, 0.75));
obj->texCoords.push_back(glm::vec2(0.5, 0.25));

Here is my object data:

struct ObjectData {
    vector <glm::vec3> vertices, normals, colors;
    vector <glm::vec2> texCoords;
    vector <GLint> indices;

    ObjectData();
};
Was it helpful?

Solution

You are using the location for the position and texture coordinate attributes. I think in the code:

//Set shader attribute data
glEnableVertexAttribArray(VBO_TEXCORD);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

You meant to pass VBO_TEXCORD for the first argument to glVertexAttribPointer.

OTHER TIPS

This part:

Mesh::Mesh(ObjectData *obj) {
    //Initialize first
    Mesh::Mesh();

    //Set object to parameter
    setObject(obj);
}

doesn't work the way you think - unlike in e.g. Java, Mesh::Mesh() doesn't call the default contructor of this.
(I'm surprised your compiler doesn't complain.)

You can either move the common initialization to a separate function that you call from both constructors or smash them together using a default parameter value like this:

Mesh::Mesh(Object* obj) {
    //Generate vertex array
    glGenVertexArrays(1, &arrayObject);

    for (int i = 0; i < VBO_COUNT; i++) {
        //Generate vertex buffer
        glGenBuffers(1, &buffers[i]);
    }

    if (obj)
      setObject(obj);
}

So the code that uses exclusively VBO_VERTEX is working and it crashes when you use VBO_VERTEX and VBO_TEXCORD at the same time?

My guess would be that the vertices and texCoords arrays don't have the same size, e.g.: object->texCoords.size() < object->vertices.size()

And then when glDrawArrays tries to fetch data from the respective arrays, vertices are okay because there are enough elements but texCoords doesn't have enough elements => access violation.

As a side note: since all your data seems to be static, you would get better performance by using a single VBO instead of two. Quoted from https://www.opengl.org/wiki/Vertex_Specification_Best_Practices:

Q: Should you create a separate VBO for each? Would you lose performance? A: If your objects are static, then merge them all into as few VBOs as possible for best performance. See above section for more details on layout considerations.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top