VBOとは?
目次
- VBOとは
- VBOがどのようなデータを格納するのか?
- VBOの作り方(従来バージョンと最新バージョン)
- VBOの値を変更してみよう
一、VBOとは
→VBO=Vertex Buffer Object
→バッファオブジェクトであり、GPU上に作られる。
→格納するデータは頂点データ(座標、色情報など)
→OpenGLにて描画するために最も基礎的部分(VBO→VAO→Vertex Puller)
VBOはVertex Buffer Object(頂点バッファ―オブジェクト)の頭文字からなる。
GPUメモリに作られるバッファオブジェクト(データを格納するメモリ上の領域)の一種である。データを格納するメモリ上の領域というのは、例えば、glGenBuffersを呼び出しでも、バッファーオブジェクトはまだ作られておらず、領域だけ確保されている状態である。
OpenGLにて描画するためには、煩雑なプロセスを辿らないと描画できない。簡単に説明すると、頂点データを持っているVBOをたくさん集めて、このようなVBOの集まりをVertex Array Object(VAO)と呼ぶ。そしてOpenGLの描画手順Graphics PipelineのVertex Pullerにより、描画に必要なだけの頂点データが取られて描画に進むという流れになる。
glDeleteBuffers
二、VBOにはどのようなデータが格納できるのか
三、VBOの作り方
方法①:
1.glGenBuffers(GLsizei n, GLuint *buffers);
こちらの関数を呼び出してバッファーのID(names)を予約する(中身が空で領域も割り当てておらず、IDだけ生成される)。nには作成する個数、buffersにはバッファ―オブジェクトのID(names)の格納先のポインタを指定する。
glGenBuffersは作成されたID(names)が連続であることを保証しない。例えば一気にバッファーオブジェクトを五個作成しても、必ずしも1から5という連続的な値が割り当てられるというわけではない。ただし、割り当てられたID(names)はglGenBuffersを呼び出した時点で必ず使用されていないことが保障されている。
glDeleteBuffersによりバッファーオブジェクトのID(names)が削除されてから、glGenBuffersではこのID(names)を再使用することが可能となる。
生成失敗することはあまりないが、個数に負の値を指定してしまうと、GL_INVALID_VALUEという値はエラーコールバックで返される。
2.glBindBuffer(GLenum target, GLuint bufffer);
glGenBuffersでID(names)が生成されただけでは、ただID(names)が確保されただけで、中身や何のバッファーオブジェクトかが分からないので、用途を指定する必要がある。そのためにはglBindBufferを使用する。
glBindBufferはバッファーオブジェクトを指定したバッファーバインディングポイントに割り当てる。targetにOpenGLが用意した定数の中から用途を指定する。bufferにはバッファーオブジェクトのID(names)へのポインタを指定する。glBindBufferは指定した用途にID(names)を割り当てる。もしID(names)が存在しなければ、新たにIDが作られ格納される。そして同じ用途を同時に二つ指定することができず、指定した用途がすでに他のバッファーオブジェクトに割り当てられている状態であれば、その割り当てが解除され、新たに指定したバッファーオブジェクトが適用される。
バッファーオブジェクトのID(names)とそのバッファーオブジェクトのコンテンツは現在使用しているGL Rendering コンテンツのローカルに配置される。
glBindBufferのtargetに指定できる用途は全部14個ある。
- GL_ARRAY_BUFFER
- GL_ATOMIC_COUNT_BUFFER
- GL_COPY_READ_BUFFER
- GL_COPY_WRITE_BUFFER
- GL_DISPATCH_INDIRECT_BUFFER
- GL_DRAW_INDIRECT_BUFFER
- GL_ELEMENT_ARRAY_BUFFER
- GL_PIXEL_PACK_BUFFER
- GL_PIXEL_UNPACK_BUFFER
- GL_QUERY_BUFFER
- GL_SHADER_STORAGE_BUFFER
- GL_TEXTURE_BUFFER
- GL_TRANSFORM_FEEDBACK_BUFFER
- GL_UNIFORM_BUFFER
具体的にどれはどんな目的なのかについて、今回は割愛させてもらう。
3.glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
glBufferDataは指定している目的用のバッファーオブジェクトのデータストアを作成する。もしこの関数を呼び出した時点で既にデータストアが存在しているのであれば、それを削除する。データストアは指定したサイズと使用方法によりつくられている。もしdataはNULLでなければ、そのdataで初期化される。初期化時にはmap pointerにNULLが設定されており、mapped accessはGL_READ_WRITEと設定される。
usageはデータストアはどのようにアクセスされるのかを指定する。usageを指定することで、バッファーオブジェクトのパフォーマンスを著しく上げることができる。usageは二つのパートに分けられる。一つはアクセスの頻度で、もう一つはアクセスの目的である。
- 頻度
STREAM データストアコンテンツは一回しか変更しない、使用回数少ない
STATIC データストアコンテンツは一回しか変更しない、使用回数多い
DYNAMIC データストアコンテンツは何度も変更する、使用回数も多い
- 目的
DRAW データストアコンテンツはアプリにより変更され、描画や画像指定として使われる
READ データストアコンテンツはGL側の読み込みデータにより変更され、アプリに要求される時にデータを転送する
COPY データストアコンテンツはGL側の読み込みデータにより変更され、描画や画像指定として使われる
4.glBindBuffer(GLenum target, 0);
バッファーオブジェクトのIDは符号なしのint型で、ID(names)に0を指定することが可能だが、デフォルトではどの用途のバッファーオブジェクトにも割り当てることができない。したがって、一般的に前の割り当てを解除する目的で0を指定する。0を指定することで、もう一つの効果として、もしデバイスは0を指定する以前のバッファーオブジェクトをサポートしているのであれば、そのバッファーオブジェクトのクライアントメモリを保存することができる。
以上の流れでバッファーオブジェクトは作成される。
方法②:
今までは方法①のやり方でVBOを作成していた。しかし、これらの関数は現代的なGPUの特性に合っておらず、GPUの性能を発揮しにくくなってきたので、OpenGL4.5では、新しい方法が実装された。
1.glCreateBuffers(GLsizei n, GLuint *buffers);
2.glNamedBufferStorage(GLuint buffer, GLsizeiptr size, const void *data, GLbitdield flags);
やっていることは方法①と変わらないが、用途の指定が必要なくなったみたい。バッファーオブジェクトを発行して、設定やデータを送り込むだけになった。
四、VBOのデータを変更してみよう
VBOはクライアント側のメモリスペースにバッファーをマッピングすれば、データの修正が可能となる。
glBindBuffer(GLenum target, GLuint bufffer);
glMapBuffer(GLenum target, GLenum access);
データを修正
glUnMapBuffer();