Home > Software engineering >  Broken Vertex Data in Mesh Class
Broken Vertex Data in Mesh Class

Time:11-04

I'm trying to implement model loading, but I'm stuck with one problem. When i try to draw a mesh (a single textured quad written by hand for test purposes) for some reason duplicated data associated with the first vertex is passed to the vertex shader (RenderDoc screen).

Here is an example using a stripped down version of my class that still exhibits this behaviour:

#define TEST_MESH

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>
#include <vector>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

struct Vertex {
    float position[3];
    float color[3];
};

class Mesh {
public:
    Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices);
    ~Mesh();
    Mesh(const Mesh&) = delete;
    Mesh& operator=(const Mesh&) = delete;
    Mesh(Mesh&& other);
    Mesh& operator=(Mesh&& other);

    void Draw(unsigned int program_id);

    std::vector<Vertex> m_Vertices;
    std::vector<unsigned int> m_Indices;

private:
    unsigned int m_VAO, m_VBO, m_EBO;

    void Setup();
    void Release();
};

int main() {
    //GLFW INIT
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //GLFW Window setup
    const unsigned int SCR_WIDTH = 800;
    const unsigned int SCR_HEIGHT = 600;

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Mesh Test", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    //GLAD INIT
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    //Shader setup:
    const char* vertex = "#version 460 core\n"
                         "layout (location = 0) in vec3 aPos;\n"
                         "layout (location = 1) in vec3 aCol;\n"
                         "out vec3 vColor;\n"
                         "void main() {\n"
                         "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                         "   vColor = aCol;\n"
                         "}\0";

    const char* fragment = "#version 460 core\n"
                           "in vec3 vColor;\n"
                           "out vec4 FragColor;\n"
                           "void main() {\n"
                           "    FragColor = vec4(vColor, 1.0);\n"
                           "}\0";

    unsigned int vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertex, NULL);
    glCompileShader(vertexShader);

    unsigned int fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragment, NULL);
    glCompileShader(fragmentShader);

    unsigned int program;
    program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    //Data:
    float floats[24] = {
        -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
         0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
         0.5f,-0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f,-0.5f, 0.0f, 1.0f, 1.0f ,0.0f };

    unsigned int uints[6] = { 0, 1, 2, 2, 3, 0 };

#ifdef TEST_MESH
    //Mesh assembly
    std::vector<Vertex> vertices;
    for (int i = 0; i < 4; i  )
        vertices.push_back(Vertex{ floats[6 * i], floats[6 * i   1], floats[6 * i   2],
                                   floats[6 * i   3], floats[6 * i   4], floats[6 * i   5] });

    std::vector<unsigned int> indices{ 0, 1, 2, 2, 3, 0 };

    Mesh mesh(vertices, indices);
#else
    unsigned int VAO, VBO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(floats), &floats[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uints), &uints[0], GL_STATIC_DRAW);

    //Positions
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    //Colors
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
#endif

    //Render loop
    while (!glfwWindowShouldClose(window)) {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

#ifdef TEST_MESH
        mesh.Draw(program);
#else
        glUseProgram(program);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
#endif
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

Mesh::Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices) {
    m_Vertices = vertices;
    m_Indices = indices;

    Setup();
}

Mesh::Mesh(Mesh&& other)
    : m_VAO(other.m_VAO), m_VBO(other.m_VBO), m_EBO(other.m_EBO)
    , m_Vertices(other.m_Vertices), m_Indices(other.m_Indices)
{
    other.m_VAO = 0;
    other.m_VBO = 0;
    other.m_EBO = 0;
}

Mesh& Mesh::operator=(Mesh&& other) {
    if (this != &other) {
        Release();

        std::swap(m_VAO, other.m_VAO);
        std::swap(m_VBO, other.m_VBO);
        std::swap(m_EBO, other.m_EBO);

        m_Vertices = other.m_Vertices;
        m_Indices = other.m_Indices;
    }
    return *this;
}

Mesh::~Mesh() {
    Release();
}

void Mesh::Release() {
    glDeleteVertexArrays(1, &m_VAO);
    glDeleteBuffers(1, &m_VBO);
    glDeleteBuffers(1, &m_EBO);
}

void Mesh::Setup() {
    glGenVertexArrays(1, &m_VAO);
    glGenBuffers(1, &m_VBO);
    glGenBuffers(1, &m_EBO);

    glBindVertexArray(m_VAO);

    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW);

    //Positions
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
    glEnableVertexAttribArray(0);
    //Colors
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
    glEnableVertexAttribArray(1);

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

void Mesh::Draw(unsigned int program_id) {
    glUseProgram(program_id);

    glBindVertexArray(m_VAO);
    glDrawElements(GL_TRIANGLES, m_Indices.size(), GL_UNSIGNED_INT, 0);
}

void processInput(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

void framebuffer_size_callback(GLFWwindow* window, int width, int 
height) {
    glViewport(0, 0, width, height);
}

CodePudding user response:

In your Mesh::Setup you have this line at the end:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

This unbinds your EBO from your VAO. Your implementation seems to treat undefined reads as zero, therefore all you see is the 0th vertex replicated six times.

Once you bound the EBO you don't need to unbind it.

(Note: unbinding GL_ARRAY_BUFFER, on the other hand, is OK. This is because the VBO is attached to the VAO at the time you call any of the *Pointer functions, not at the time you bind it to GL_ARRAY_BUFFER.)

(Note: Since you use the latest OpenGL version, I strongly recommend that you use the Direct State Access (DSA) functions. In this case you would bind the EBO with

glVertexArrayElementBuffer(m_VAO, m_EBO);

call, which I think would make the mistake much more obvious. See a quick reference of VAO state and the recommended functions to use to manipulate it.)

  • Related