A vertex array object (also known as VAO) can be bound just like a vertex buffer object and any subsequent vertex attribute calls from that point on will be stored inside the VAO. This has the advantage that when configuring vertex attribute pointers you only have to make those calls once and whenever we want to draw the object, we can just bind the corresponding VAO. This makes switching between different vertex data and attribute configurations as easy as binding a different VAO. All the state we just set is stored inside the VAO.

https://learnopengl.com/Getting-started/Hello-Triangle

Core OpenGL requires that we use a VAO so it knows what to do with our vertex inputs. If we fail to bind a VAO, OpenGL will most likely refuse to draw anything.

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

#include "Shader.h"

void size_callback(GLFWwindow* window, int width, int height);
GLFWwindow* create_GLFWwindow();
void init_glad();


int main() {
    GLFWwindow* window = create_GLFWwindow();
    init_glad();

    Shader shader; 
    shader.compile();
    shader.bind();

    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                DEFINE VERTICES FOR VAO1                  <|
    // |>----------<>----------<>----------<>----------<>----------<|

    // Create and bind Vertex Array Object
    unsigned int VAO1;
    glGenVertexArrays(1, &VAO1);
    glBindVertexArray(VAO1);

        float vertices[]{
            -0.5f, -0.5f, // 0 - bottom left 
             0.5f, -0.5f, // 1 - bottom right
             0.5f,  0.5f, // 2 - top right
            -0.5f,  0.5f, // 3 - top left

        };
        unsigned int indices[] = {  // note that we start from 0!
            0, 1, 2,  // first Triangle
            0, 2, 3   // second Triangle
        };

        unsigned int VBO;
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO); 
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

        unsigned int EBO;
        glGenBuffers(1, &EBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


        // we give meanings to our data. Because it is just a bunch of byte
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

    glBindVertexArray(0); // Unbind VAO


    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                DEFINE VERTICES FOR VAO2                  <|
    // |>----------<>----------<>----------<>----------<>----------<|


    unsigned int VAO2;
    glGenVertexArrays(1, &VAO2);
    glBindVertexArray(VAO2);


        float vertices2[]{
             0.5f,  0.5f, // 0 - bottom left 
             1.0f,  0.5f, // 1 - bottom right
             1.0f,  1.0f, // 2 - top right
             0.5f,  1.0f, // 3 - top left

        };
        unsigned int indices2[] = {  // note that we start from 0!
            0, 1, 2,  // first Triangle
            0, 2, 3   // second Triangle
        };

        unsigned int VBO2;
        glGenBuffers(1, &VBO2);
        glBindBuffer(GL_ARRAY_BUFFER, VBO2);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices2, GL_STATIC_DRAW);

        unsigned int EBO2;
        glGenBuffers(1, &EBO2);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices2, GL_STATIC_DRAW);


        // we give meanings to our data. Because it is just a bunch of byte
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

    glBindVertexArray(0); // Unbind VAO2






    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                       RENDER LOOP                        <|
    // |>----------<>----------<>----------<>----------<>----------<|

    while (!glfwWindowShouldClose(window)) {

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


        glBindVertexArray(VAO1);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glBindVertexArray(VAO2);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);



        glfwSwapBuffers(window);
        glfwPollEvents();
    }

}



void size_callback(GLFWwindow* window, int width, int height) {
    std::cout << "New width: " << width << "\theight: " << height << std::endl;
    glViewport(0, 0, width, height);
}

GLFWwindow* create_GLFWwindow(){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                    CREATE GLFW WINDOW                    <|
    // |>----------<>----------<>----------<>----------<>----------<|

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(640, 480, "My 3D app", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        //return EXIT_FAILURE;
    }
    glfwMakeContextCurrent(window);

    glfwSetFramebufferSizeCallback(window, size_callback); // we bind callback to our size_callback func. 

    return window;
}

void init_glad(){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>               LOAD OPENGL FUNCTION POINTERS              <|
    // |>----------<>----------<>----------<>----------<>----------<|

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        exit(EXIT_FAILURE);
    }

}


Refactor

main.cpp

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

#include "Shader.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"

void size_callback(GLFWwindow* window, int width, int height);
GLFWwindow* create_GLFWwindow();
void init_glad();


int main() {
    GLFWwindow* window = create_GLFWwindow();
    init_glad();

    Shader shader; 
    shader.compile();
    shader.bind();

    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                      DEFINE VERTICES                     <|
    // |>----------<>----------<>----------<>----------<>----------<|

    float vertices[]{
        -0.5f, -0.5f, // 0 - bottom left 
            0.5f, -0.5f, // 1 - bottom right
            0.5f,  0.5f, // 2 - top right
        -0.5f,  0.5f, // 3 - top left

    };
    unsigned int indices[] = { 
        0, 1, 2,  // first Triangle
        0, 2, 3   // second Triangle
    };
    unsigned int layout[]{ 2, GL_FLOAT };

    VertexArray va; va.Bind();
    VertexBuffer vb(&vertices[0], sizeof(vertices));
    IndexBuffer ib(indices, sizeof(indices));

    vb.setLayout(layout,sizeof(layout));
    va.Unbind();
        

    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                       RENDER LOOP                        <|
    // |>----------<>----------<>----------<>----------<>----------<|

    va.Bind();

    while (!glfwWindowShouldClose(window)) {
        glClearColor(0.2f, 0.2f, 0.2f, 1.0f); 
        glClear(GL_COLOR_BUFFER_BIT); 

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

}



void size_callback(GLFWwindow* window, int width, int height) {
    std::cout << "New width: " << width << "\theight: " << height << std::endl;
    glViewport(0, 0, width, height);
}

GLFWwindow* create_GLFWwindow(){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                    CREATE GLFW WINDOW                    <|
    // |>----------<>----------<>----------<>----------<>----------<|

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(640, 480, "My 3D app", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        //return EXIT_FAILURE;
    }
    glfwMakeContextCurrent(window);

    glfwSetFramebufferSizeCallback(window, size_callback); // we bind callback to our size_callback func. 

    return window;
}

void init_glad(){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>               LOAD OPENGL FUNCTION POINTERS              <|
    // |>----------<>----------<>----------<>----------<>----------<|

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        exit(EXIT_FAILURE);
    }

}

Vertex Array

#pragma once
class VertexArray
{
	unsigned int ID;
public:
	VertexArray();
	~VertexArray();

	void Bind() const;
	void Unbind() const;
};
#include "VertexArray.h"
#include "glad/glad.h"

VertexArray::VertexArray(){
    glGenVertexArrays(1, &ID);
}

VertexArray::~VertexArray(){
    glDeleteVertexArrays(1, &ID);
}

void VertexArray::Bind() const{
    glBindVertexArray(ID);
}

void VertexArray::Unbind() const{
    glBindVertexArray(0);
}

Vertex Buffer

#pragma once

class VertexBuffer
{
	unsigned int ID;
	unsigned int getSize(int count, const unsigned int& gType);
public:
	VertexBuffer(const void* data, unsigned int size);
	~VertexBuffer();

	void Bind() const;
	void Unbind() const;

	void setLayout(unsigned int* layout, unsigned int size);
};

#include "VertexBuffer.h"
#include "glad/glad.h"

unsigned int VertexBuffer::getSize(int count, const unsigned int& gType){
    
    switch (gType){
    case GL_FLOAT:
        return count * sizeof(GL_FLOAT);
    }
    return 0;
}

VertexBuffer::VertexBuffer(const void* data, unsigned int size){
    glGenBuffers(1, &ID);
    glBindBuffer(GL_ARRAY_BUFFER, ID);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

VertexBuffer::~VertexBuffer(){
    glDeleteBuffers(1, &ID);
}

void VertexBuffer::Bind() const {
    glBindBuffer(GL_ARRAY_BUFFER, ID);
}

void VertexBuffer::Unbind() const {
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void VertexBuffer::setLayout(unsigned int* layout, unsigned int size) {

    // calculate stride
    int stride{};
    for (int i = 0; i < size; i += 2)
        stride += getSize(layout[i], layout[i + 1]);


    // set vertex attrib pointers 
    int offset{};
    for (int i = 0, j = 0; i < size; i += 2, j++) {
        glVertexAttribPointer(j, layout[i], GL_FLOAT, GL_FALSE, stride, (void*)offset);
        glEnableVertexAttribArray(j);

        offset += getSize(layout[i], layout[i + 1]);
    }

}

Index Buffer

#pragma once
class IndexBuffer
{
	unsigned int ID;
public:
	IndexBuffer(const void* data, unsigned int size);
	~IndexBuffer();

	void Bind() const;
	void Unbind() const;
};

#include "IndexBuffer.h"
#include "glad/glad.h"

IndexBuffer::IndexBuffer(const void* data, unsigned int size){
    glGenBuffers(1, &ID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

IndexBuffer::~IndexBuffer(){
    glDeleteBuffers(1, &ID);
}

void IndexBuffer::Bind() const{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ID);
}

void IndexBuffer::Unbind() const{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *