Vertex Buffer Object(VBO)について
VBOとは†
OpenGL - Vertex arrayはOpenGLのAPIの呼び出しを減らすことで,
パフォーマンスを大きく改善することができるものの,
頂点データは描画呼び出し毎にクライアント側のメモリからサーバ(GPU)側のメモリ(ビデオカードのメモリ)に転送されるという問題もあります.
一方,GPU側で完全に格納されたデータで呼び出しのたびに実行されるものとしてディスプレイリストがあります.
ディスプレイリストではAPI呼び出しのオーバーヘッドをなくすことで高速な描画を可能としています.
しかし,ディスプレイリストは一度コンパイルされれば,ディスプレイリスト内のデータを変更することはできません.
これらの問題を解決するのが Vertex Buffer Object(VBO) です.
VBOは頂点データを格納するGPU側のメモリに置かれたバッファです.
クライアント側のメモリ上の配列と同じように扱え,
glVertexPointerなどに直接渡すことができます.
また,ディスプレイリストと違い,クライアント側のメモリスペースにバッファをマッピングすれば,
クライアントからの読込みや変更が可能となります.
VBO作成手順†
VBOの作成にはGL拡張をいくつか使います.
GLEWなどをインクルード,リンクするようにしてください(参照:GLEWについて).
VBOの作成はテクスチャを作成するのと同じような手順です.
- glGenBuffersでバッファ生成
void glGenBuffers(GLsizei n, GLuint *buffers);
nに生成する数を指定し,bufferに生成したVBO名が返ってくる.
- glBindBufferでバッファをバインド
void glBindBuffer(GLenum target, GLuint buffer);
targetにはVBOに何のデータ(頂点,インデックス,ピクセル)を格納されているのかを指定する
(GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
bufferはVBO名.
- glBufferDataでクライアントからバッファを転送
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
targetは2と同じで,sizeには転送するサイズ,dataにはクライアントのデータ領域のポインタを指定する.
もしdataにNULLが指定されたらデータは転送されない.usageは格納されたデータの使われ方で,
GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, GL_DYNAMIC_COPY
のどれかを指定する.
- STATIC : VBOデータ変更なし.(modified once and used many times)
- DYNAMIC : VBOデータが頻繁に変更される.(modified repeatedly and used many times)
- STREAM : VBOデータが毎フレーム変更される.(modified once and used once or a few times)
- DRAW : VBOデータはアプリケーションから転送され,OpenGL描画に用いられる.(application -> GL)
- READ : VBOデータはGLから転送され,アプリケーションで読み込まれる.(GL -> application)
- COPY : VBOデータはGLから転送され,OpenGL描画に用いられる.(GL -> GL)
他に範囲データ転送やVBO破棄命令などがある.
- glBufferSubData
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);
- glDeleteBuffers
void glDeleteBuffers(GLsizei n, const GLuint * buffers);
VBOを使った描画例†
まず,VBOを作成します.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | vector<Vec3> vertices; vertices.push_back(Vec3(0, 0, 0));
vertices.push_back(Vec3(1, 0, 0));
vertices.push_back(Vec3(1, 1, 0));
vertices.push_back(Vec3(0, 1, 0));
vector<int> indices; indices.push_back(0);
indices.push_back(1);
indices.push_back(2);
indices.push_back(0);
indices.push_back(2);
indices.push_back(3);
glGenBuffers(1, &vrtVBO);
glBindBuffer(GL_ARRAY_BUFFER, vrtVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*3*sizeof(double), (GLdouble*)&vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &idxVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(int), &indices[0], GL_STATIC_DRAW);
|
この処理は頂点,インデックスデータが変わらなければ1回だけでいいです.
次に描画時には,
1
2
3
4
5
6
7
8
9
10
11
12
| | glBindBuffer(GL_ARRAY_BUFFER, vrtVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxVBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
glBindBufferでVBOをバインドして,あとはVertex arrayと同じですが,
データの場所のポインタには0を指定します(オフセット値0ということ).
使い終わったら削除します.
1
2
| | glDeleteBuffers(1, &vrtVBO);
glDeleteBuffers(1, &idxVBO);
|
VBOデータの変更†
VBOはクライアント側のメモリスペースにバッファをマッピングすれば,データの修正が可能となります.
データの修正は以下の手順で行います.
データの修正の例は以下.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | void ModifyVBO(void)
{
glBindBuffer(GL_ARRAY_BUFFER, vrtVBO);
double *ptr = (double*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
static double ang = 0.0;
if(ptr){
for(int i = 1; i <= 2; ++i){
ptr[3*i] = cos(RX_TO_RADIANS(ang));
ptr[3*i+2] = sin(RX_TO_RADIANS(ang));
}
glUnmapBuffer(GL_ARRAY_BUFFER);
ang = (ang < 360) ? ang+1 : 0.0;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
|
|