Advanced Data
버퍼들을 좀 더 흥미로운 방법과 texture들을 통해 shader에 많은 양의 data를 전달하는 흥미로운 방법이 존재한다.
이런 버퍼함수들과, 많은 양의 데이터를 저장하기 위해 텍스처 객체를 사용하는 방법을 다룰 것이다.
OpenGL에서의 buffer는 특정 메모리를 관리하는것이다.
- 특정 buffer target에 바인딩하여 의미를 부여해준다.
GL_ARRAY_BUFFER
에 바인딩 하면 이 버퍼는 vertex array buffer- 이 동일한 버퍼를 다른 것에 바인딩 가능
GL_ELEMENT_ARRAY_BUFFER
에 바인딩 가능.- OpenGL은 내부적으로 target에 대해 buffer를 저장하고 이 buffer들을 따로 처리한다.
glBufferData
지금까지 메모리를 할당해주고, 이 메모리에 데이터를 삽입해주는
glBufferData
함수를 사용하여 buffer 객체에 의해 관리되는 메모리를 채워 왔음.- 여기에
NULL
값을 넣는다면, 이 함수는 메모리 할당만 해주고, 데이터를 채워 넣지 않는다. - 이는 특정 메모리 크기를 예약하고, 나중에 이 buffer를 채우려고 할 경우에 유용하다.
- 여기에
glBufferSubData
- 대신
glBufferSubData
함수를 사용하여, buffer의 특정 부분을 채우는 것 또한 가능하다.- 이 함수는 buffer target, offset, data size, 실제 data를 파라미터로 받는다.
- offset 지정이 이 함수의 특징
- 이는 buffer메모리의 특정 부분에만 삽입/ 수정할 수 있도록 해준다.
- 이 buffer는 충분한 메모리가 할당되어 있어야하므로, glBufferSubData 함수를 호출하기 전에
glBufferData
함수를 꼭 호출해야한다.
1
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // Range: [24, 24 + sizeof(data)]
glMapBuffer
pointer에게 buffer의 메모리를 요청하고 데이터를 buffer에 직접 복사하는 방법
이 함수를 사용하면 OpenGL은 현재 바인딩된 buffer의 메모리를 가리키고 있는 포인터를 리턴한다.
1
2
3
4
5
6
7
8
9
10
11
float data[] = {
0.5f, 1.0f, -0.35f
[...]
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// get pointer
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// now copy data into memory
memcpy(ptr, data, sizeof(data));
// make sure to tell OpenGL we're done with the pointer
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer
함수를 통해 포인터 작업이 끝났다고 OpenGL에게 알려줄 수 있음.- 이 포인터는 무효화됨.
- 이 함수는 OpenGL이 buffer에 데이터를 성공적으로 매핑할 수 있는 경우 GL_TRUE를 리턴한다.(unless the data store’s contents became corrupted during the time the data store was mapped)
glMapBuffer
함수를 사용하는 것은 먼저 임시 메모리에 저장하지 않고 직접 매핑하기에 유용하다.
Batching vertex attributes
glVertexAttributePointer
함수를 사용하여 vertex array buffer 내용의 attribute layout을 지정할 수 있었음.- vertex array buffer내부에 attribute들을 끼워넣었음.(위치, 법선, 텍스처 좌표들을 메모리에 서로 옆에 배치)
이와 같은 방법말고 다른 방법으로 지정할 수 있다.
attribute 유형마다 큰 덩어리로 묶는 방법
- 파일로부터 vertex 데이터를 불러올 때, 일반적으로 위치, 법선, 텍스처 좌표 배열을 얻는다.
- 이를 하나의 큰 배열로 결합하는것은 큰 비용
- 각 유형으로 묶는것이 더 쉬움
- 파일로부터 vertex 데이터를 불러올 때, 일반적으로 위치, 법선, 텍스처 좌표 배열을 얻는다.
glBufferSubData
로 이를 쉽게 구현가능하다.
1
2
3
4
5
6
7
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);
이를 통해 직접적으로 하나의 buffer에 배열을 옮길 수 있다.
또한 이렇게 할 경우, vertex attribute pointer를 수정해야 한다.
1
2
3
4
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의 크기와 동일하다.(바로 옆에 같은 유형의 데이터가 있으므로)이런 방법을 사용하는 것은 OpenGL에 즉각적인 효과는 없다.
- 단지 좀더 vertex attribue를 설정하는게 좀 더 체계적인 방법임.
Copying buffers
- 하나의 버퍼에 데이터가 채워지면, 이 데이터들을 다른 버퍼와 공유, 복사 할 수 있다.
glCopyBufferSubData
- 한 버퍼에서 다른 버퍼로 비교적 수월하게 복사함
- 이 함수의 프로토타입은 다음과 같다.
1
2
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset,
GLintptr writeoffset, GLsizeiptr size);
readtarget
,writetarget
: 복붙할 버퍼 타겟 지정- buffer target:
VERTEX_ARRAY_BUFFER
=>VERTEX_ELEMENT_ARRAY_BUFFER
가능 - 그러면 현재 해당 버퍼 대상에 바인딩된 버퍼들이 영향을 받는다.
- buffer target:
하지만 둘다 같은 buffer target일 경우.(같은 유형에 2개의 버퍼 바인딩 불가능)
- 추가적인 buffer target을 사용한다.
GL_COPY_READ_BUFFER
,GL_COPY_WRITE_BUFFER
- 이 새로운 버퍼 타겟에 버퍼들을 바인딩하고 파라미터로 넘겨주면 된다.
주어진
size
만큼readoffset
에서부터 읽고writeoffset
을 시작지점으로 붙여넣는다.
1
2
3
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, 8 * sizeof(float));
- 또한
writetarget
버퍼만 바인딩하여 수행 가능
1
2
3
4
float vertexData[] = { ... };
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, 8 * sizeof(float));