Buffer Object

Date:     Updated:

카테고리:

태그:

Main Reference
- Learn OpenGL
- Rinthel Kwon - OpenGL Lecture


Vertex Buffer Object (VBO)

CPU에 있는 vertex정보들을(position, normal, texture coord, …) GPU가 접근 가능한 메모리에 저장

uint32_t m_vertexBuffer;

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0f,
};
 
glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, vertices, GL_STATIC_DRAW);
void glGenBuffers(GLsizei n, GLuint *buffers)
  • 새로운 버퍼 오브젝트 생성
  • n : 생성할 버퍼 객체의 개수
  • buffers : 생성된 버퍼 객체의 이름(식별자)을 저장할 배열의 포인터


void glBindBuffer(GLenum target, GLuint buffer);
  • 지정된 target에 buffer를 바인딩.
  • target은 GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER


void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
  • 데이터를 업로드할 버퍼 객체 (Bind에서 사용한 target과 동일한 값)
  • usage : 버퍼의 사용 패턴을 나타내는 flag
    • GL_STATIC_XXXX: 딱 한번만 세팅되고 앞으로 계속 쓸 예정
    • GL_DYNAMIC_XXXX: 앞으로 데이터가 자주 바뀔 예정
    • GL_STREAM_XXXX: 딱 한번만 세팅되고 몇번 쓰다 버려질 예정
    • GL_XXXX_DRAW : GPU에서 읽는 일반적인 경우
    • GL_XXXX_COPY : 주로 CPU에서 읽히는 경우 (드믄 경우)
    • GL_XXXX_READ : GPU에서 GPU로 복사하는 경우
    • 메모리 위치 지정할 때 사용되는듯..


Vertex Array Object (VAO)

Example1 Example2
hierarchy vertex_array_objects_ebo
  • VBO에 들어있는 데이터를 어떻게 읽어야 하는지 구조(Layout)를 알려주는 객체
  • 일반적으로 position에 관한 VBO, color에 관한 VBO, texcoor에 관한 VBO, … 등등을 묶어 하나의 VAO로 관리
  • DX11 의 input_element_description과 같은 역할


uint32_t m_vertexArrayObject;

glGenVertexArrays(1, &m_vertexArrayObject);
glBindVertexArray(m_vertexArrayObject);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);

// vertex shader
layout (location = 0) in vec3 aPos;
  • VBO와 마찬가지로 Generate & Bind
  • glEnableVertexAttribArray(0) : VAO에 포함된 VBO중 0번째 VBO 사용!
  • 나중에 shader에서 location으로 해당되는 index 가져올 수 있음


void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);

image

  • index : Enable에서 지정한 index
  • size : 현재 나의 속성(pos, color, ..) 하나 당 포함되는 데이터 개수
  • type : 데이터의 속성 (주로 float, index버퍼에서는 int)
  • normalized : [0, 1]로 normalize했는지 여부
  • stride : vertex 사이의 간격
  • pointer : vertex에서 나의 속성까지의 offset


순서 주의

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0f,
};

glGenVertexArrays(2, &m_vertexArrayObject);
glBindVertexArray(m_vertexArrayObject);

// position
glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, vertices, GL_STATIC_DRAW);

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);

// color
// ...
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);

1) VAO Binding 2) VBO Binding 3) Vertex Attribute Setting


Draw Triangle

void glDrawArrays(GLenum primitive, GLint offset, GLsizei count);
Primitives Result
GL_GeometricPrimitives image
  • 현재 설정된 program, VBO, VAO로 그림을 그림


Element Buffer Object (EBO)

대부분의 경우 삼각형 단위로 폴리곤을 그리면 많은 정점들이 중복되기 때문에 index를 이용해 object를 렌더링한다. EBO는 DX11의 index buffer와 같은 역할을 수행하며 사용 방법은 VBO와 동일하다.

float vertices[] = {
  0.5f, 0.5f, 0.0f, // top right
  0.5f, -0.5f, 0.0f, // bottom right
  -0.5f, -0.5f, 0.0f, // bottom left
  -0.5f, 0.5f, 0.0f, // top left
};
uint32_t indices[] = { // note that we start from 0!
  0, 1, 3, // first triangle
  1, 2, 3, // second triangle
};

glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, vertices, GL_STATIC_DRAW);

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);

// Index Buffer Setting
glGenBuffers(1, &m_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * 6, indices, GL_STATIC_DRAW);

// ...

// Render
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

image

  • 추가적으로 Index 버퍼를 세팅한 다음, glDrawArrays 대신 glDrawElements 사용


Refactoring

지금까지 살펴본 OpenGL의 Buffer 객체들을 편리하게 관리하기 위해 리팩토링을 진행하자. 각 인스턴스를 관리할 수 있는 클래스를 설계하면 스마트 포인터 기능을 활용하여 리소스를 관리할 수 있고, 인스턴스 생성 & 데이터 접근을 막을 수 있다. 또 하나 중요한 것은 생성이나 바인딩 실패 시 컴파일 오류를 내뱉을 수 있다는 점이다.

// Buffer.h
CLASS_PTR(Buffer)
class Buffer {
public:
    static BufferUPtr CreateWithData(uint32_t bufferType, uint32_t usage, const void* data, size_t dataSize);
    ~Buffer();
    uint32_t Get() const { return m_buffer; }
    void Bind() const;

private:
    Buffer() {}
    bool Init(uint32_t bufferType, uint32_t usage, const void* data, size_t dataSize);
    uint32_t m_buffer { 0 };
    uint32_t m_bufferType { 0 };
    uint32_t m_usage { 0 };
};
  • VBO, EBO를 관리하는 Buffer 클래스 생성


// VertexLayout.h
CLASS_PTR(VertexLayout)
class VertexLayout {
public:
    static VertexLayoutUPtr Create();
    ~VertexLayout();

    uint32_t Get() const { return m_vertexArrayObject; }
    void Bind() const;
    void SetAttrib(
        uint32_t attribIndex, int count,
        uint32_t type, bool normalized,
        size_t stride, uint64_t offset) const;
    void DisableAttrib(int attribIndex) const;

private:
    VertexLayout() {}
    void Init();
    uint32_t m_vertexArrayObject { 0 };
};
  • VAO를 관리하는 VertexLayout 클래스


// context.cpp
m_vertexLayout = VertexLayout::Create();

m_vertexBuffer = Buffer::CreateWithData(GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices, sizeof(float) * 12);

m_vertexLayout->SetAttrib(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);

m_indexBuffer = Buffer::CreateWithData(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, indices, sizeof(uint32_t) * 6);
  • 이제 버퍼에 관련된 객체들은 클래스를 통해 관리



맨 위로 이동하기

OpenGL 카테고리 내 다른 글 보러가기

댓글 남기기