Advanced Buffer

Date:     Updated:

카테고리:

태그:

Main Reference
- Learn OpenGL
- Rinthel Kwon - OpenGL Lecture


Advanced Buffer

void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage);

앞서 VBO를 다룰 때 usage 파라미터에는 GL_DYNAMIC_XX, GL_XXX_COPY 등 여러 옵션이 있었다는 것을 언급만 하고, 특정 버퍼를 어떻게 접근하고 수정하는지는 설명하지 않았다. 이번 포스팅에서는 위 내용을 다뤄보자.


일부 갱신

void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void * data);

// Range: [24, 24 + sizeof(data)]
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data);

glBufferSubData를 이용해 버퍼의 일부 데이터를 갱신할 수 있다. 해당 함수를 이용하면 버퍼의 기존 데이터를 유지하면서 특정 부분만 수정한다. 이때 data는 nullptr일 수 없으며 크기는 명시한 size와 같아야 한다.


직접 접근

float data[] = {
  0.5f, 1.0f, -0.35f
  ...
};

glBindBuffer(GL_ARRAY_BUFFER, buffer);

void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

memcpy(ptr, data, sizeof(data));

glUnmapBuffer(GL_ARRAY_BUFFER);
  • void* glMapBuffer(GLenum target, GLenum access); : target에 바인딩된 버퍼의 주소를 받아옴
    • access에는 read_only, write_only, read_write
  • 버퍼 수정이 끝났으면 glUnmapBuffer로 끝났음을 알려주고, 포인터는 반납해야 함
    • mapping 도중에는 gpu가 해당 버퍼를 접근할 수 없기 때문에 바로바로 해주는게 좋음


일부 복사

void glCopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);


float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
  • 버퍼사이의 데이터를 간단하게 복사하는 함수 존재
  • target 버퍼가 만드시 COPY_XXXX_BUFFER로 바인딩될 필요는 없음(일반적인 버퍼도 가능)


텍스처 일부 갱신

glBindBuffer(GL_TEXTURE_2D, texture);
glTexSubImage2D(GL_TEXTURE_2D, level,
  xoffset, yoffset, width, height,
  GL_RGBA, GL_UNSIGNED_BYTE, imageptr);
  • 텍스처 역시 버퍼와 마찬가지로, 이미 GPU 메모리로 올라간 데이터를 일부 수정하는 함수가 존재한다.


Uniform Buffer

// Render()...
m_textureProgram->Use();
m_windowTexture->Bind();
m_textureProgram->SetUniform("tex", 0);

modelTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.5f, 4.0f));
transform = projection * view * modelTransform;
m_textureProgram->SetUniform("transform", transform);
m_plane->Draw(m_textureProgram.get());

modelTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.2f, 0.5f, 5.0f));
transform = projection * view * modelTransform;
m_textureProgram->SetUniform("transform", transform);
m_plane->Draw(m_textureProgram.get());

modelTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.4f, 0.5f, 6.0f));
transform = projection * view * modelTransform;
m_textureProgram->SetUniform("transform", transform);
m_plane->Draw(m_textureProgram.get());

Render() 함수에서 여러 오브젝트를 그리려고 하면 코드가 굉장히 번잡해진다. 특히 Transform 변환 중 model은 그렇다 치더라도 view, projection 등은 모든 오브젝트가 공유하는 변환이다. 이러한 공통된 transform matrix를 오브젝트마다 shader로 전달해야 하니 코드가 복작해지는 것이다. 이런 경우 사용하는 Uniform Buffer Object를 이용하면 모든 shader에서 사용할 수 있는 global buffer 개념이 존재한다.


// vertex shader
#version 330 core

layout (location = 0) in vec3 aPos;

layout (std140, binding = 2) uniform Matrices {
  mat4 projection;
  mat4 view;
};

uniform mat4 model;

void main() {
  gl_Position = projection * view * model * vec4(aPos, 1.0);
}
  • UBO 생성 방법은 VBO와 마찬가지로 glGenBuffers() 함수 이용
    • 단, 바인딩 할 때 target을 GL_UNIFORM_BUFFER로 지정
  • shader에서는 layout xxx uniform으로 받음


Uniform Block Layout

layout (std140) uniform ExampleBlock
{
    float value;
    vec3  vector;
    mat4  matrix;
    float values[3];
    bool  boolean;
    int   integer;
};

위와 같은 uniform block을 gpu로 넘겨준다고 해보자. 이때 opengl은 하드웨어 최적화를 위해 변수들의 순서를 재배열하게 된다. 예를 들어 float 변수는 vec3 변수 뒤에 놓는 식이다. 이는 GPU 메모리 접근 효율성을 위해 4/16 바이트 단위로 데이터를 정렬하는 것이다. 문제는 데이터의 순서가 바뀌면 프로그래머 입장에서 사용하기가 곤란해진다. 이럴 때 사용하는 것이 std140 layout 이다. 이 방식은 메모리 공간 효율성은 다소 떨어지지만, 프로그래머가 예측 가능한 큰 장점이 존재한다. std140의 규칙은 아래와 같다.


Type Layout Rule
Scalar(int, bool, ..) 4 byte
VecN 16 byte
Array of Scalar/VecN 16 * (size) byte
MatN 16 * N byte
Struct 16 byte 크기로 정렬


layout (std140) uniform ExampleBlock {
                   // base alignment // aligned offset
  float value;     // 4              // 0
  vec3 vector;     // 16             // 16 (multiple of 16: 4->16)
  mat4 matrix;     // 16             // 32 (column 0)
                   // 16             // 48 (column 1)
                   // 16             // 64 (column 2)
                   // 16             // 80 (column 3)
  float values[3]; // 16             // 96 (values[0])
                   // 16             // 112 (values[1])
                   // 16             // 128 (values[2])
  bool boolean;    // 4              // 144
  int integer;     // 4              // 148
};



맨 위로 이동하기

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

댓글 남기기