[Learn OpenGL 번역] 5-7. 고급 OpenGL - 고급 Data
고급 Data
고급 OpenGL/고급 Data
우리는 OpenGL에서 데이터를 저장하기 위해 광범위하게 buffer들을 사용해왔습니다. buffer들을 다루는 좀 더 흥미로운 방법과 texture들을 통해 shader에 많은 양의 data를 전달하는 흥미로운 방법이 존재합니다. 이 강좌에서 좀 더 흥미로운 buffer 함수들과 많은 양의 데이터를 저장하기 위해 텍스처 객체를 사용하는 방법을 다룰 것입니다.
OpenGL에서의 buffer는 특정 메모리를 관리하는 것 그 이상 이하도 아닙니다. 특정
지금까지 메모리를 할당해주고 이 메모리에 데이터를 삽입해주는 NULL
값을 집어넣는다면 이 함수는 메모리 할당만 해주고 데이터를 채워넣지 않습니다. 이는 우리가 먼저 특정 메모리 크기를 reserve(예약)해놓고 나중에 이 buffer를 채우려고 할 경우에 유용합니다.
하나의 함수를 호출하여 전체 buffer를 채우는 것 대신
glBufferSubData (GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 범위: [24, 24 + sizeof(data)]
Buffer에 데이터를 집어넣는 또다른 방법은 pointer에게 buffer의 메모리를 요청하고 데이터를 buffer에 직접 복사하는 방법입니다.
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));
// OpenGL에게 이 포인터와 작업을 마친다고 알려줌
glUnmapBuffer (GL_ARRAY_BUFFER);
Vertex attributes 묶기
우리가 또 할 수 있는 일은 모든 벡터 데이터들을 끼워넣는 것이 아니라 attribute 유형마다 큰 덩어리로 묶을 수 있다는 것입니다. 123123123123
이런 식으로 끼워넣는 것이 아니라 111122223333
이런식으로 묶는 방법이 있습니다.
파일로부터 vertex 데이터를 불러올 때 일반적으로 위치의 배열, 법선의 배열, 텍스처 좌표의 배열을 얻습니다. 이 배열들을 끼워넣어진 형태로 하나의 큰 배열로 결합하는 것은 꽤 큰 비용이 듭니다. 묶는 방법을 취하는 것은 쉬운 해결방법입니다.
float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };
// fill buffer
glBufferSubData (GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData (GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData (GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);
이 방법으로 우리는 먼저 그들을 처리할 필요 없이 직접적으로 attribute 배열들을 하나의 buffer에 옮길 수 있습니다. 물론 이들을 결합하여 하나의 큰 배열로 만들수 있으며
또한 이 변화를 반영하기 위해 vertex attribute pointer를 수정해야 합니다.
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));
glVertexAttribPointer (
2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));
Stride
파라미터는 vertex attribute의 크기와 동일하다는 것을 알아두세요. 다음 vertex attribute 벡터가 바로 이 벡터의 3(혹은 2) 요소 다음에 존재하기 때문입니다.
이는 vertex attribute 설정과 지정에대해서 또다른 접근법을 제공해줍니다. 이런 방법을 사용하는 것은 OpenGL에 즉각적인 효과는 없습니다.vertex attribute를 설정하는 것이 대부분 좀 더 체계화가 되어있는 방법입니다. 여러분이 어떤 방법을 사용할지는 여러분의 선호도와 응용 프로그램의 유형에 달려있습니다.
Buffer 복사
여러분의 buffer가 데이터로 채워지기만하면 이 데이터들을 다른 buffer들과 공유할 수 있고 이 buffer의 내용을 다른 buffer에 복사할 수도 있을 것입니다.
void glCopyBufferSubData (GLenum readtarget, GLenum writetarget, GLintptr readoffset,
GLintptr writeoffset, GLsizeiptr size);
readtarget
, writetarget
파라미터들은 우리가 붙여넣기할, 복사할 buffer target를 지정합니다. 예를 들어 buffer target을 따로 따로 지정하여 VERTEX_ARRAY_BUFFER buffer를 복사하여 VERTEX_ELEMENT_ARRAY_BUFFER buffer에 붙여 넣을 수 있습니다. 이러한 buffer target에 현재 바인딩되어 있는 buffer들은 영향을 받게 됩니다.
하지만 읽고싶은 buffer와 작성하고 싶은 buffer 둘다 같은 vertex array buffer라면 어떨까요? 우리는 동시에 동일한 buffer target에 2개의 buffer를 바인딩할 수는 없습니다. 이 이유때문에 OpenGL은 2개의 추가적인 buffer target을 제공해줍니다. GL_COPY_READ_BUFFER와 GL_COPY_WRITE_BUFFER가 바로 그것이죠. 이 새로운 buffer target에 buffer들을 바인딩하고 readtarget
, writetarget
파라미터에 넘겨줍니다.
그러면 size
의 데이터를 readoffset
으로부터 읽고 writetarget
buffer에 writeoffset
을 시작지점으로 작성하게 됩니다. 이 예제는 다음과 같습니다.
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));
또한 우리는 writetarget
buffer만 바인딩하여 수행할 수도 있습니다.
float vertexData[] = { ... };
glBindBuffer (GL_ARRAY_BUFFER, vbo1);
glBindBuffer (GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData (GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
Buffer를 다루는 방법에 대한 추가적인 지식으로 우리는 이미 이들을 좀 더 흥미로운 방법으로 사용할 수 있습니다. OpenGL에 대해서 더 많이 알수록 새로운 buffer method들은 더 유용해질 것입니다. 다음 강좌에서는