c++openglgraphicsvbovao

VAO isn't rendering my simple triangle, what could be causing this?


I've already looked all over the internet for the past couple days and I cannot find a fix to this problem. I believe I've got the basic debugging stuff setup yet I get no errors telling me what I've done wrong. This is my first attempt at a renderer, and I'm also trying to use a OOP/DOD (Data oriented design) structure. I think the error is just something small that I am blind to, or I'm completely doing this wrong... please help if you can.

Also I'm using shaders, EBOs, VBOs, linking to a VAO (I think) Additional information: I'm using SDL + GLAD(opengl 4.6, core)

If you need me to reply with anything else that could help you help me, let me know.

Here is my console output: screenshot

All I see on the window: screenshot

Here are my files (that might have the error):

vertex shader

#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}

fragment shader

#version 460 core
out vec4 FragColor;

in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0f);
}

main.cpp (the window)

#include <loader.h>
using namespace testProgram;

int close(SDL_Window* window, SDL_GLContext glContext) {
    IMG_Quit();
    
    SDL_DestroyWindow(window);
    SDL_GL_DeleteContext(glContext);
    SDL_Quit();

    return 0;
}

// Simple debugger callback, will most likely modify in the future.
static void APIENTRY openglCallbackFunction(
    GLenum source,
    GLenum type,
    GLuint id,
    GLenum severity,
    GLsizei length,
    const GLchar* message,
    const void* userParam
){
    (void)source; (void)type; (void)id; 
    (void)severity; (void)length; (void)userParam;
    fprintf(stderr, "%s\n", message);
    if (severity==GL_DEBUG_SEVERITY_HIGH) {
        fprintf(stderr, "Aborting...\n");
        abort();
    }
}

std::vector<GLfloat> vertices {
    // positions         // colors
    0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left
    0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top 
};

std::vector<GLuint> indices {
    0, 1, 2
};

std::vector<int> vertexAttribSizes {
    3
};

int main(int argc, char* argv[]) {

    // Variables
    bool quit = false;

    // Initializations
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_PNG);
    setGLAttributes();

    // Creating the window
    SDL_Window* window = SDL_CreateWindow("test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, winW, winH, flags);
    SDL_GLContext glContext = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, glContext);

    SDL_Surface* icon = IMG_Load("images/icon.png");
    setIcon(window, icon);

    // Setting up OpenGL
    if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
        printf("Failed to initialize GLAD\n");
        return -1;
    }

    glViewport(0, 0, winW, winH);

    // Enable the debug callback
    glEnable(GL_DEBUG_OUTPUT);
    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
    glDebugMessageCallback(openglCallbackFunction, nullptr);
    glDebugMessageControl
    (
        GL_DONT_CARE, 
        GL_DONT_CARE, 
        GL_DONT_CARE, 
        0, NULL, true
    );

    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    Renderer::Queue queue;
    Renderer::Shader shader;
    Renderer::Object object;

    shader.Update("CUT OUT THE DIRECTORY FOR THIS POST (because it doesn't matter)", "CUT OUT THE DIRECTORY FOR THIS POST (because it doesn't matter)");
    object.Update(vertices, indices, shader.id, "triangle", vertexAttribSizes);
    object.Attach(queue);

    while(!quit) {
        SDL_Event event;
        mouseX = event.motion.x;
        mouseY = event.motion.y;
        
        while (SDL_PollEvent(&event) != 0) {
            inputCollection(event, quit);
        }

        SDL_SetWindowHitTest(window, hitCallback, 0);

        glClearColor(0.085f, 0.085f, 0.085f, 0.f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        queue.Render();

        SDL_GL_SwapWindow(window);
    }

    // Closes all processes
    object.Detach(queue);
    close(window, glContext);

    return 0;
}

renderer.h (it is included in loader.h, which is included in main.cpp. loader.h I believe doesn't contain the issue, so I dont want to pollute this post with even more code haha)

// Includes
#include <glad/glad.h>

#include <stdio.h>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <ctype.h>
#include <fstream>
#include <cstring>
#include <sstream>

// Renderer
namespace Renderer {

    class Shader {

        private:

            // Read the shader's source code.
            std::string readFile(const char* _path);

            // Check the shader.
            bool checkShader(GLuint &_shader);

            // Check the program.
            bool checkProgram(GLuint &_program);

        public:

            const char* vertexPath;
            const char* fragmentPath;
            GLuint id;

            void Update(const char* _vertexPath, const char* _fragmentPath); // Update the shader's parameters.

            void Use() {  glUseProgram(id); } // Use the shader.

    };

    class Queue {

        public:

            GLuint VAO;

            void listObjects(); // List all objects in the queue (BY NAME, INDEX IS THE ID FOR ALL VALUES/PARAMETERS)
            GLuint getObject(std::string objName); // Grab an object from the queue (BY NAME, INDEX IS THE ID FOR ALL VALUES/PARAMETERS) [Returns the id of the object]
            bool findObject(std::string objName); // Returns true or false if the object is found.


            void Render(); // Render all objects in the queue. (Sets VAOs)

            std::vector<std::vector<GLfloat>> vertices;
            std::vector<std::vector<int>> indices;
            std::vector<std::vector<int>> vertexAttribs;

            std::vector<int> shaderProgramIDs;
            std::vector<std::string> names;

    };

    class Object {

        public:

            GLuint VBO, EBO;

            std::vector<GLfloat> vertices;
            std::vector<int> indices;
            std::vector<int> vertexAttribSizes; // This is the sizes of the vertex attributes, not the values.

            GLuint shaderProgramID;
            std::string name;

            // Set the object's parameters.
            void Update(
                std::vector<GLfloat> _vertices, 
                std::vector<GLuint> _indices, 
                int _shaderProgramID, 
                std::string _name,
                std::vector<int> _vertexAttribSizes
            );

            void Attach(Queue &queue);
            void Detach(Queue &queue);

    };
    
};

renderer.cpp

#include <renderer.h>

using namespace Renderer;

// List all objects in the queue.
void Queue::listObjects() {
    for (auto name : names) {
        std::cout << name << std::endl;
    }
}

// Returns true or false if the object is found.
bool Queue::findObject(std::string objName) {
    auto index = std::find(names.begin(), names.end(), objName);
    if (index != names.end()) {
        return true;
    } else {
        return false;
    }
}

// Returns the object id.
GLuint Queue::getObject(std::string objName) {
    auto index = std::find(names.begin(), names.end(), objName);
    if (findObject(objName)) {
        return index - names.begin();
    } else {
        return -1;
    }
}

// Renders all objects linked to the current queue.
void Queue::Render() {
    for (int i = 0; i < names.size(); i++) {
        glUseProgram(shaderProgramIDs[i]);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, indices[i].size(), GL_UNSIGNED_INT, 0);
    }
}

std::string Shader::readFile(const char* _path) {
    std::string content;
    std::ifstream fileStream(_path, std::ios::in);

    if (!fileStream.is_open()) {
        std::cerr << "Could not read file " << _path << ". File does not exist." << std::endl;
        return "";
    }

    std::string line = "";
    while (!fileStream.eof()) {
        std::getline(fileStream, line);
        content.append(line + "\n");
    }

    fileStream.close();
    return content;
}

bool Shader::checkShader(GLuint &_shader) {
    GLint success;
    glGetShaderiv(_shader, GL_COMPILE_STATUS, &success);
    if(!success) {
        GLint maxLength = 0;
        glGetShaderiv(_shader, GL_INFO_LOG_LENGTH, &maxLength);

        std::vector<GLchar> errorLog(maxLength);
        glGetShaderInfoLog(_shader, maxLength, &maxLength, &errorLog[0]);

        std::cout << "Shader compilation failed: " << std::endl;
        std::cout << &errorLog[0] << std::endl;
        return false;
    }
    return true;
}

bool Shader::checkProgram(GLuint &_program) {
    GLint success;
    glGetProgramiv(_program, GL_LINK_STATUS, &success);
    if(!success) {
        GLint maxLength = 0;
        glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &maxLength);

        std::vector<GLchar> errorLog(maxLength);
        glGetProgramInfoLog(_program, maxLength, &maxLength, &errorLog[0]);

        std::cout << "Program linking failed: " << std::endl;
        std::cout << &errorLog[0] << std::endl;
        return false;
    }
    return true;
}

void Shader::Update(
    const char* _vertexPath, 
    const char* _fragmentPath
){
    GLuint vertex, fragment;
    vertex = glCreateShader(GL_VERTEX_SHADER);
    fragment = glCreateShader(GL_FRAGMENT_SHADER);

    vertexPath = _vertexPath;
    fragmentPath = _fragmentPath;

    std::string vertexString = readFile(_vertexPath);
    std::string fragmentString = readFile(_fragmentPath);

    const char *vertexSource = vertexString.c_str();
    const char *fragmentSource = fragmentString.c_str();

    glShaderSource(vertex, 1, &vertexSource, NULL);
    glCompileShader(vertex);
    checkShader(vertex);

    glShaderSource(fragment, 1, &fragmentSource, NULL);
    glCompileShader(fragment);
    checkShader(fragment);

    this->id = glCreateProgram();
    glAttachShader(this->id, vertex);
    glAttachShader(this->id, fragment);
    glLinkProgram(this->id);
    checkProgram(this->id);
    
    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

void Object::Attach(Queue &queue) {
    queue.names.push_back(this->name);
    GLuint id = queue.getObject(this->name);
    queue.shaderProgramIDs.insert(queue.shaderProgramIDs.begin()+id, this->shaderProgramID);
    queue.vertices.insert(queue.vertices.begin()+id, this->vertices);
    queue.indices.insert(queue.indices.begin()+id, this->indices);
    queue.vertexAttribs.insert(queue.vertexAttribs.begin()+id, this->vertexAttribSizes);

    glGenBuffers(1, &this->VBO);
    glGenBuffers(1, &this->EBO);

    glGenVertexArrays(1, &queue.VAO);
    glBindVertexArray(queue.VAO);

    glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(this->vertices), &this->vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), &this->indices, GL_STATIC_DRAW);

    int stride = 3;
    for (int i = 0; i < this->vertexAttribSizes.size(); i++) {
        stride+=this->vertexAttribSizes[i];
    }

    // Positions
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // Other attributes
    int totalSize = 3;
    for (int i = 0; i < this->vertexAttribSizes.size(); i++) {
        glVertexAttribPointer(i+1, this->vertexAttribSizes[i], GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(totalSize * sizeof(float)));
        glEnableVertexAttribArray(i+1);
        totalSize+=this->vertexAttribSizes[i];
    }

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

void Object::Detach(Queue &queue) {
    GLuint id = queue.getObject(this->name);
    queue.names.erase(std::remove(queue.names.begin(), queue.names.end(), this->name), queue.names.end());
    queue.vertices.erase(queue.vertices.begin()+id, queue.vertices.begin()+id+this->vertices.size());
    queue.indices.erase(queue.indices.begin()+id, queue.indices.begin()+id+this->indices.size());
    queue.vertexAttribs.erase(queue.vertexAttribs.begin()+id, queue.vertexAttribs.begin()+id+this->vertexAttribSizes.size());

    glDeleteBuffers(1, &this->VBO);
    glDeleteBuffers(1, &this->EBO);
}

void Object::Update(
    std::vector<GLfloat> _vertices, 
    std::vector<GLuint> _indices, 
    int _shaderProgramID, 
    std::string _name,
    std::vector<int> _vertexAttribSizes
){
    // TODO: Clear all vectors, also detach and reattach automatically.

    for (int i = 0; i < _vertices.size(); i++) {
        vertices.push_back(_vertices[i]);
    }
    for (int i = 0; i < _indices.size(); i++) {
        indices.push_back(_indices[i]);
    }
    for (int i = 0; i < _vertexAttribSizes.size(); i++) {
        vertexAttribSizes.push_back(_vertexAttribSizes[i]);
    }

    shaderProgramID = _shaderProgramID;
    name = _name;
}

Solution

  •     glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(this->vertices), &this->vertices, GL_STATIC_DRAW);
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), &this->indices, GL_STATIC_DRAW);
    

    this->vertices is a std::vector. You are uploading raw pointer values where there should be actual data in the buffer.

        glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->vertices.size(), this->vertices.data(), GL_STATIC_DRAW);
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * this->indices.size(), this->indices.data(), GL_STATIC_DRAW);