개발/OpenGL / / 2022. 3. 6. 13:24

OpenGL 공부일지 - OpenGL Super Bible Buffer - 1

반응형

Buffers

OpenGL에서 여러모로 활용 가능한 연속적인 메모리 공간이고, 이름으로 구별된다.

사용하기 위해서는 이름을 예약하고, 그 이름으로 메모리 할당과 데이터 저장 작업을 거친다. 버퍼 객체를 할당하는 메모리는 data store이다. 

버퍼의 이름으로 binding후 context에 attach키는데, binding에는 버퍼의 내용이 vertex shader의 입력으로 공급되도록 하거나, 쉐이더가 사용할 변수의 값으로 지정하거나, shader가 생성하는 데이터를 저장공간으로 사용하는 등 다양하다.

Creating Buffers and Allocating Memory

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

glBufferData는 버퍼 객체를 사용해서 메모리를 할당한다.

target변수로 OpenGL에게 할당할 버퍼가 어떤 타깃에 바인딩 될지 알려준다. 

GL_ARRAY_BUFFER 바인딩 포인트는 vertex 속성들을 담은 데이터를 저장하는 포인트이다. 

size는 OpenGL 버퍼에 특정 초깃값을 지정하기 위해 사용되고 NULL을 지정할 수 있다.

usage는 버퍼의 사용 목적을 결정한다.

 

7판에서는 glBufferStorage라는 함수로 변경되었다.

void glBufferStorage(GLenum target,
    GLsizeiptr size,
    const void* data,
    GLbitfield flags);
void glNamedBufferStorage(GLuint buffer,
    GLsizeiptr size,
    const void* data,
    GLbtifield flags);

usage가 flags로 변경되었고, 사용목적에 따라 다음 표와 같은 flag들이 있다.

// The type used for names in OpenGL is GLuint
GLuint buffer;

// Create a buffer
glCreateBuffers(1, &buffer); // glGenBuffer

// Specify the data store parameters for the buffer
glNamedBufferStorage( // glBufferData
    buffer, // Name of the buffer
    1024 * 1024, // 1 MiB of space
    NULL, // No initial data
    GL_MAP_WRITE_BIT); // Allow map for writing
    
// Now bind it to the context using the GL_ARRAY_BUFFER binding point
glBindBuffer(GL_ARRAY_BUFFER, buffer);

6판에서와 함수 이름들이 약간씩 다르지만 같은 역할을 하는 코드이다. glCreateBuffer는 6판의 glGenBuffer와 동일하고, glNameBufferStorage는 glBufferData와 동일하다.

buffer를 생성하고, glCreateBuffer로 1메가의 용량을 갖게 초기화한다. 

glBindBuffer에서 Opengl에 버퍼 사용을 알리는데, 버퍼에 참조하기 위해서 GL_ARRAY_BUFFER를 타깃으로 사용한다. 

glNamedBufferStorage에서는 NULL포인터를 세번째 인자로 전달하였는데, NULL이 아닌 포인터를 전달하면, 해당 데이터로 버퍼를 초기화할 수 있다.

 

void glBufferSubData(GLenum target,
    GLintptr offset,
    GLsizeiptr size,
    const GLvoid * data);
void glNamedBufferSubData(GLuint buffer,
    GLintptr offset,
    GLsizeiptr size,
    const void * data);

glBufferSubData함수로 Opengl로 데이터를 보내 복사하는 방법도 있다. 위 코드에서는 같은 함수이지만, 위의 함수는 6판에서, 밑의 함수는 7판에서 사용한다고 보면 된다.

 

// This is the data that we will place into the buffer object
static const float data[] =
{
    0.25, -0.25, 0.5, 1.0,
    -0.25, -0.25, 0.5, 1.0,
    0.25, 0.25, 0.5, 1.0
};

// Put the data into the buffer at offset zero
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data);

위와 같은 방식으로 버퍼객체에 데이터를 저장한다.

 

// This is the data that we will place into the buffer object
static const float data[] =
{
    0.25, -0.25, 0.5, 1.0,
    -0.25, -0.25, 0.5, 1.0,
    0.25, 0.25, 0.5, 1.0
};

// Get a pointer to the buffer's data store
void * ptr = glMapNamedBuffer(buffer, GL_WRITE_ONLY);

// Copy our data into it...
memcpy(ptr, data, sizeof(data));

// Tell OpenGL that we're done with the pointer
glUnmapNamedBuffer(GL_ARRAY_BUFFER);

다른 방법으로 위와 같이 glMapBuffer함수를 사용하여, 메모리 포인터를 Opengl에 요청하여 데이터를 복사하는 방법이 있다.

glMapBuffer는 모든 데이터가 준비되지 않은 상황에서 함수를 호출할 때 유용하다. 데이터를 직접 생성하거나 파일로부터 읽는 경우처럼 말이다. 

glBufferSubData는 glBufferData에 초기화 포인터를 전달하는데, 데이터를 생성하거나 읽어 임시적으로 메모리에 담고 opengl에 버퍼 객체의 내부에 해당 복사본을 유지하게 만든다고 한다. 아마도 glBufferData로 특정 offset을 초기화 한 다음 glBufferSubData로 data를 해당 offset에 저장하는 방식인 듯 하다.

위 두 함수 둘 다 data의 복사본을 만들어 vertex shader에 데이터를 전달할 수 있다.

 

void *glMapBuffer(GLenum target,
	GLenum usage);
void *glMapNamedBuffer(GLuint buffer,
	GLenum usage);
    
void glUnmapBuffer(GLenum target);
void glUnmapNamedBuffer(GLuint buffer);

glMapBuffer는 unmap함수를 통해 opengl에서 복사본 매핑을 제거할 수 있다.

 

void *glMapBufferRange(GLenum target,
    GLintptr offset,
    GLsizeiptr length,
    GLbitfield access);
void *glMapNamedBufferRange(GLuint buffer,
    GLintptr offset,
    GLsizeiptr length,
    GLbitfield access);

7판에서는 위와 같은 glMapBufferRange라는 함수를 소개하는데, glMapBuffer와는 달리 특정한 버퍼 객체의 범위를 offset과 length 인자를 통해 지정하여 할당한다. 

Filling and Copying Data in Buffers

glBufferData로 할당했으면 이제 데이터를 채울 차례이다.

void glClearBufferSubData(GLenum target, // 6th
    GLenum internalformat,
    GLintptr offset,
    GLsizeiptr size,
    GLenum format,
    GLenum type,
    const void * data);
void glClearNamedBuffeSubData(GLuint buffer, // 7th
    GLenum internalformat,
    GLintptr offset,
    GLsizeiptr size,
    GLenum format,
    GLenum type,
    const void * data);

위 glClearBufferSubData는 버퍼에 저장하려는 데이터가 const일 경우 효율적이다.

glClearBufferSubData는 버퍼 객체에 담을 변수 포인터를 internal format으로 변경한다.

해당 내용을 byte단위의 offset과 size로 버퍼 데이터 스토어 영역에 복사한다. 

format과 type은 opengl에 데이터가 가리키는 포인터의 정보이다. format의 경우 특정 채널에 저장하기 위해 GL_RED, GL_RG, GL_RGB, GL_RGBA 중 하나로 지정가능하다. type은 컴포넌트의 데이터 타입을 의미한다. 

위와 같이 C에서 상응되는 데이터타입이 type인자에 들어간다.

 

void glCopyBufferSubData(GLenum readtarget,
    GLenum writetarget,
    GLintptr readoffset,
    GLintptr writeoffset,
    GLsizeiptr size);

void glCopyNamedBufferSubData(GLuint readBuffer,
    GLuint writeBuffer,
    GLintptr readOffset,
    GLintptr writeOffset,
    GLsizeiptr size);

glCopyBufferSubData를 이용하면 데이터를 버퍼 간 공유하거나 다른 버퍼로 복사할 수 있다.

readtarget에서 writetarget으로 버퍼 데이터를 복사하는데, 사전에 바인딩이 되어있어야 하기 때문에, 같은 target에 바인딩 된 두 버퍼 간에는 복사하지 못한다. 예를 들어 GL_ARRAY_BUFFER에 바인딩 된 두 버퍼간에는 불가능하다.

 

이러한 버퍼 복사의 용도로 opengl에서는 GL_COPY_READ_BUFFER와 GL_COPY_WRITE_BUFFER를 제공한다. 이 두 target을 사용하여 다른 버퍼 타깃과 헷갈리지 않도록 하는 것이 용이하다.

readoffset, writeoffset은 opengl에서 버퍼의 어디를 읽어야할지 결정한다.

size로 그 용량을 알려준다.

offset인자와 size의 타입인 GLintptr, GLsizeiptr은 포인터 변수를 담기 위한 상당히 큰 크기의 정수형 타입이다.

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유