To Paint a World

OpenGL - 3. Buffer Objects 본문

3D Engine/OpenGL

OpenGL - 3. Buffer Objects

Polariche 2025. 1. 2. 12:15

BufferObject

#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <vector>

template<typename T>
class BufferObject {

private:
    unsigned int _id;
    int _type;
    std::vector<T> * _data;

public:
    BufferObject( int type, std::vector<T> * data);
    ~BufferObject();

    void Bind() const;
    void Unbind() const;
    void BufferData() const;
    void SetAttribPointer(unsigned int vao, int dtype) const;

    inline std::vector<T> * GetVector() const {return _data; }
    inline std::vector<T> * GetSize() const {return _data->size(); }

};

 

 

 

BufferObject 생성과 삭제

template<typename T>
BufferObject<T>::BufferObject(int type, std::vector<T> * data) {
    if (!!data)
        _data = data;
    else
        _data = new std::vector<T>();

    _type = type;
    
    glGenBuffers(1, &_id);
}

template<typename T>
BufferObject<T>::~BufferObject() {
    glDeleteBuffers(1, &_id);
}

 

glGenBuffers 를 호출하여 버퍼 오브젝트를 생성한다. 

glGenBuffers(1, &_id) 는 buffer object 를 1개 생성하여 buffer object name 을 _id 의 값에 배정한다.

 

_type 에는 glBindBuffer 의 target 을 배정한다. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER 등)

_data 에는 glBindBuffer 를 통해 GPU 로 보낼 데이터의 벡터를 지정한다.

 

BufferObject 바인딩 & 언바인딩

template<typename T>
void BufferObject<T>::Bind() const {
    glBindBuffer(_type, _id);
}

template<typename T>
void BufferObject<T>::Unbind() const {
    glBindBuffer(_type, 0);
}

 

Bind() 는 glBindBuffer 를 호출하여, buffer object name 이 _id 의 값인 buffer object 를 _target 에 바인딩한다.

Unbind() 는 _target 을 언바인딩한다. buffer object name = 0 으로 설정하면 unbind 된다.

 

BufferObject 데이터 버퍼링

template<typename T>
void BufferObject<T>::BufferData() const {
    Bind();

    glBufferData(_type, _data->size() * sizeof(T), _data->data(), GL_STATIC_DRAW);

    Unbind();
}

 

glBufferData 를 호출하여, _type_data 값을 업데이트 해준다.

 

다음 argument 대로 호출한다.

glBufferData(vbo 의 타겟, 복사할 데이터의 크기 (byte 단위), 데이터의 시작 포인터, GL_STATIC_DRAW)

 

BufferObject - Vertex Attribute Pointer 세팅

template<typename T>
void BufferObject<T>::SetAttribPointer(unsigned int vao, int dtype) const {
    Bind();

    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);

    switch(dtype) {
        case GL_FLOAT: 
            glVertexAttribPointer(0, sizeof(T) / sizeof(float), dtype, GL_FALSE, sizeof(T), nullptr);
            break;

        case GL_DOUBLE: 
            glVertexAttribPointer(0, sizeof(T) / sizeof(double), dtype, GL_FALSE, sizeof(T), nullptr);
            break;
    }
    
    glBindVertexArray(0);

    Unbind();
}

 

만약 Buffer Object 가 Vertex Buffer Object 라면 (_type == GL_ARRAY_BUFFER), 배열은 {x1, y1, z1, x2, y2, z2, ..., xn, yn, zn} 와 같은 구조를 띄고 있게 된다. 여기서 한 vertex 는 {x1, y1, z1} 등과 같이 여러 개의 attribute 의 묶음으로 정의될 수 있다. 이처럼 vertex attribute 의 정보를 저장하는 구조체가 Vertex Array Object 이다.

 

먼저 glBindVertexArray 를 통해 VAO 를 바인딩하고, glVertexAttribPointer 를 호출하여 현재 바인딩된 VBO 를 어떤 attribute 로 분할할지 지정하여 VAO 에 attribute 정보를 저장한다.

 

다음과 같은 argument 를 패스하여 호출한다.

glVertexAttribPointer(0, attribute 의 수, GL 자료형, GL_FALSE, vertex 의 크기 (바이트), nullptr);

 

 

MainApp - Vertex Buffer Object 를 통해 정점 그리기

typedef pair<float, float> Point;
class MainApp : GLApp {
// ...

protected:
    vector<Point> points;

    Point* selected_point = NULL;

protected:
    unsigned int vao;

    BufferObject<Point>* vbo;
};

 

Point 자료형을 선언한다.

MainApp 에 VAO 와 VBO 를 추가한다. VBO 는 Point 구조체를 참조하는 BufferObject 로 생성한다.

 

bool MainApp::Initialize() {
    // init GLApp functionalities
    GLApp::InitOpenGL();
    GLApp::InitCallbacks();

    // init render objects
    glGenVertexArrays(1, &vao);

    vbo = new BufferObject<Point>(GL_ARRAY_BUFFER, &points);
    vbo->SetAttribPointer(vao, GL_FLOAT);
    
    // init points
    points.push_back({0.0f, 0.5f});
    points.push_back({-0.5f, -0.5f});
    points.push_back({0.5f, -0.5f});
}

 

Vertex Array Object 를 생성하여 이름을 vao 에 저장하고, points 데이터와 연결된 Vertex Buffer Object 를 생성한다.

glBindBuffer 의 타겟이 GL_ARRAY_BUFFER 인 것이 Vertex Buffer Object 이다.

위에서 정의한 SetAttribPointer 함수를 호출하면 자동으로 vertex attribute 가 VAO 에 저장된다.

 

void MainApp::Draw() {
    vbo->BufferData();

    glBindVertexArray(vao);

    vbo->Bind();
    glPointSize(10.0f);
    glDrawArrays(GL_POINTS, 0, points.size());
    vbo->Unbind();
    
    glBindVertexArray(0);
}

 

각 드로우 콜마다,

1. VBO 의 BufferData 를 호출하여 데이터를 GPU 에 업로드한다.

2. VAO 와 VBO 를 바인딩한다.

3. glDrawArrays 를 호출하여, 각 vertex 를 그린다.

 

 

빌드 시 다음과 같이 삼각형의 정점이 그려진 것을 확인할 수 있다.

 

 

MainApp - Index Buffer Object 를 통해 간선 그리기

typedef pair<float, float> Point;
typedef pair<unsigned int, unsigned int> Edge;
class MainApp : GLApp {
// ...

protected:
    vector<Point> points;
    vector<Edge> edges;

    Point* selected_point = NULL;

protected:
    unsigned int vao;

    BufferObject<Point>* vbo;
    BufferObject<Edge>* ibo;
};

 

Edge 자료형을 선언하고, Edge 의 vector 와 Index Buffer Object 를 생성한다.

 

bool MainApp::Initialize() {
    // init GLApp functionalities
    GLApp::InitOpenGL();
    GLApp::InitCallbacks();

    // init render objects
    glGenVertexArrays(1, &vao);

    vbo = new BufferObject<Point>(GL_ARRAY_BUFFER, &points);
    vbo->SetAttribPointer(vao, GL_FLOAT);

    ibo = new BufferObject<Edge>(GL_ELEMENT_ARRAY_BUFFER, &edges);

    // init points & edges
    points.push_back({0.0f, 0.5f});
    points.push_back({-0.5f, -0.5f});
    points.push_back({0.5f, -0.5f});

    edges.push_back({0,1});
    edges.push_back({1,2});
    edges.push_back({2,0});

    return true;
}

 

Index Buffer Object 의 타겟은 GL_ELEMENT_ARRAY_BUFFER 이다.

Vertex Buffer Object 와 달리, 별도의 attribute 설정을 하지 않는다.

 

void MainApp::Draw() {
    vbo->BufferData();
    ibo->BufferData();

    glBindVertexArray(vao);

    vbo->Bind();
    glPointSize(10.0f);
    glDrawArrays(GL_POINTS, 0, points.size());
    vbo->Unbind();

    ibo->Bind();
    glLineWidth(2.0f);
    glDrawElements(GL_LINES, 2 * edges.size(), GL_UNSIGNED_INT, nullptr);
    ibo->Unbind();
    
    glBindVertexArray(0);
}

 

Index Buffer Object 를 바인딩하고, glDrawElements 를 호출하여 간선을 그린다.

 

 

'3D Engine > OpenGL' 카테고리의 다른 글

OpenGL - 5. Uniforms  (0) 2025.02.18
OpenGL - 4. Shaders  (1) 2025.01.06
OpenGL - 2. Input Callbacks  (0) 2024.12.26
OpenGL - 1. Initialization  (3) 2024.12.26