頂点バッファとは、頂点座標、頂点カラー、テクスチャ座標、頂点インデックスなどを格納するバッファです。多数の頂点を持つモデルなどを頂点シェーダに処理させる場合は頂点バッファを使用してください。頂点バッファを使用しない場合は CPU で重い処理(頂点アレイの並べ替えなど)が行われ、パフォーマンスが著しく低下する恐れがあります。
6.1. オブジェクトの生成
バッファオブジェクトを glGenBuffers()
で生成します。
void glGenBuffers(GLsizei n, GLuint* buffers);
n
個のバッファオブジェクトを生成して、buffers
にその名前(オブジェクト名)を格納します。
6.2. オブジェクトの指定
バッファオブジェクトを関連付ける頂点バッファの種類を glBindBuffer()
で指定します。この関数の呼び出し以降、各種頂点バッファへの処理は指定されたバッファオブジェクトに対して行われるようになります。
void glBindBuffer(GLenum target, GLuint buffer);
target
には頂点バッファの種類を指定します。buffer
には glGenBuffers()
で生成したバッファオブジェクトを指定します。buffer
に未生成のオブジェクト名が指定された場合、そのオブジェクト名のバッファオブジェクトの生成も同時に行われます。
target に指定する値 |
頂点バッファの種類 |
---|---|
|
頂点座標、頂点カラー、法線などのバッファ |
|
|
|
頂点ステートコレクション(「6.6. 頂点ステートコレクション」を参照) |
6.3. バッファの確保
glBufferData()
でバッファの領域を確保し、頂点データをロードします。
void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
target
に指定する値は glBindBuffer()
の target
と同じです(表 6-1)。
data
と size
には格納する頂点データとそのサイズを渡します。data
に 0(NULL
)を指定した場合は領域の確保のみが行われます。
usage
には GL_STATIC_DRAW
を渡さなければなりません。
glBufferData()
の target
に特別なフラグを論理和で渡すことで、GPU のアクセス先や領域確保時の処理を指定することができます。詳細については、「3DS プログラミングマニュアル - グラフィックス応用編」「メインメモリに置かれたデータの使用」を参照してください。
6.4. バッファの書き換え
glBufferData()
で確保したバッファの一部分を書き換えるには glBufferSubData()
を使用します。
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
target
に指定する値は glBindBuffer()
の target
と同じです(表 6-1)。
offset
には書き換える部分へのオフセットを指定します。
data
と size
には書き込むデータとそのサイズを渡します。
6.5. バッファの破棄
不要になったバッファオブジェクトは glDeleteBuffers()
で破棄します。
void glDeleteBuffers(GLsizei n, const GLuint* buffers);
buffers
に格納されている n
個のオブジェクト名で指定されたバッファオブジェクトを破棄します。
6.6. 頂点ステートコレクション
頂点ステートコレクションは 3DS が独自に導入したもので、バッファオブジェクトと頂点バッファの関連付けを記録します。頂点ステートコレクションを使用することで、バッファオブジェクトと頂点バッファの関連付けと頂点属性の設定をまとめて行うことができます。
頂点ステートコレクションはバッファオブジェクトと名前空間を共有していますので、glGenBuffers()
、glBindBuffer()
、glDeleteBuffers()
を使用して生成・指定・破棄を行います。
6.6.1. 頂点ステートコレクションの生成
頂点ステートコレクションは特殊なバッファオブジェクトとして動作します。そのためバッファオブジェクトと同じように、glGenBuffers()
で頂点ステートコレクションとして使用するオブジェクトを生成します。
6.6.2. 頂点ステートコレクションの指定
頂点ステートコレクションとして使用するバッファオブジェクトを指定するには glBindBuffer()
を使用します。target
に GL_VERTEX_STATE_COLLECTION_DMP
を渡して呼び出してください。デフォルトでは、名前 0(buffer
に 0 を渡した状態)のオブジェクトが頂点ステートコレクションとなっています。
呼び出し後は、頂点バッファ(GL_ARRAY_BUFFER
、GL_ELEMENT_ARRAY_BUFFER
)への glBindBuffer()
によるバッファオブジェクトの関連付けや、glEnableVertexAttribArray()
、glDisableVertexAttribArray()
、glVertexAttrib{1234}{fv}()
、glVertexAttribPointer()
による頂点属性の設定が頂点ステートコレクションに記録されていきます。同じ頂点バッファへの関連付けは上書きされ、頂点ステートコレクションへの記録はほかの頂点ステートコレクションに切り替えられるまで行われます。
頂点ステートコレクションを切り替えると、頂点バッファとバッファオブジェクトとの関連付けは頂点ステートコレクションに記録されているオブジェクトにすべて切り替わります。
6.6.3. 頂点ステートコレクションの破棄
バッファオブジェクトと同じように、glDeleteBuffers()
で頂点ステートコレクションを破棄することができます。頂点ステートコレクションを破棄しても、記録されているバッファオブジェクトの頂点バッファとの関連付けに影響はありません。
使用中の頂点ステートコレクションへの glDeleteBuffers()
の呼び出しでは、すぐに頂点ステートコレクションが破棄されません。ほかの頂点ステートコレクションに切り替えられるまで使用状態のまま残ります。デフォルトの頂点ステートコレクションは破棄できません。デフォルトの頂点ステートコレクションに対する glDeleteBuffers()
の呼び出しは無視されます。
6.7. 頂点バッファの使用例
頂点バッファと glDrawElements()
を使用して三角形を 1 つ描画するコード例を、配列の定義、バッファの確保、描画の 3 つの部分に分けて紹介します。
GLuint triIndexID, triArrayID; GLushort triIndex[1 * 3] = { 0, 1, 2 }; GLfloat triVertex[3 * 4] = { 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, 1.0f }; GLfloat triVertexColor[3 * 3] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
glGenBuffers(1, &triArrayID); glBindBuffer(GL_ARRAY_BUFFER, triArrayID); glBufferData(GL_ARRAY_BUFFER, sizeof(triVertex) + sizeof(triVertexColor), 0, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(triVertex), triVertex); glBufferSubData(GL_ARRAY_BUFFER, sizeof(triVertex), sizeof(triVertexColor), triVertexColor); glGenBuffers(1, &triIndexID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triIndexID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * sizeof(GLushort), triIndex, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, triArrayID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)sizeof(triVertex)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triIndexID); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
6.8. 頂点データの配置に関する制約
頂点バッファに格納された頂点データを使用して描画を行う場合、glVertexAttribPointer()
で設定する頂点データの配置には以下のハードウェアによる制約があります。これらの制約に抵触した場合、glDrawArrays()
または glDrawElements()
の呼び出し時に GL_INVALID_OPERATION
のエラーを生成します。
- 各頂点データは、自身の型のサイズでアライメントされていなければなりません。
- 各頂点データのストライドは、同じ構造体に含まれる頂点データのうち、最もサイズの大きい型のサイズの倍数でなければなりません。
- 上記 2 つの制約を満たすために最小限必要なパディングより大きいサイズのパディングを頂点データのうしろに挿入する場合、その頂点データの最後尾から最も近い 4 バイト境界の次にある 4 バイト境界に続く頂点データを配置しなければなりません。
コンパイラが自動的にパディングを挿入するため、コーディングの段階で意識しなくても、上 2 つの制約には抵触しないようになっていることもあります。
下記のコード例では、extraPadding2
を挿入しなければ最後の制約に抵触してしまいます。
struct tagVertex { GLshort position[3]; GLshort extraPadding1; GLshort extraPadding2[2]; GLshort color[4]; };
1 回の glDrawArrays()
または glDrawElements()
の呼び出しで使用する頂点属性および頂点インデックスには、頂点バッファを使用する頂点データと使用しない頂点データを混在させることができません。混在させた場合は GL_INVALID_OPERATION
のエラーを生成します。
6.8.1. glDrawElements() のみの制約
以下の条件をすべて満たす場合、頂点アレイの格納方法による制限によって GL_INVALID_OPERATION
のエラーが生成されます。
- 頂点バッファを使用している。
- 頂点属性を 12 個使用している。
- すべての頂点属性を頂点アレイとして使用している。
(すべての頂点属性に対してglEnableVertexAttribArray()
が呼ばれている)
-
glDrawElements()
で描画している。
上記をすべて満たす場合、少なくとも 2 個の頂点属性をインターリーブドアレイとして配置しなければなりません。つまり、12 個の独立アレイを同時に頂点属性として使用することはできません。
複数の頂点属性を構造体として定義し、頂点アレイを構造体の配列として配置したものをインターリーブドアレイと呼びます。これに対し、1 個の頂点属性を個別の配列として配置したものを独立アレイと呼びます。