OpenGL ES 2.0で波紋を作る#2 前提知識2
前提知識2では、シェーダの効率的な描画について解説します。
【ポイント】
・VBO
・IBO
・描画
【効率的な描画】
OpenGLには効率の良いデータ転送があります。これは、高速に描画するためには重要なデータ転送方式です。
・VBO(Vertex Buffer Object)・・・ 頂点バッファオブジェクト
・IBO(Index Buffer Object) ・・・ インデックスバッファオブジェクト
・VAO(Vertex Array Object) ・・・ 頂点情報と状態を保持するオブジェクト
など
今回は、VBO、IBOについて解説します。
【VBO】
あらかじめVRAM(GPUが利用できるメモリ領域)に頂点情報を転送することで、その後に発生する無駄なデータ転送を減らします。これにより効率的な描画ができます。
【IBO】
ポリゴンを作成すると、重複する頂点情報が多く作成され無駄なデータが発生します。
IBOは、頂点情報に番号(インデックス)を付けて、描画時にその番号を指定することで重複するデータをなくします。これにより効率的な描画ができます。
VBOとIBOを取り入れることで、VBOであらかじめ頂点情報を設定しておき、IBOで頂点情報を効率よく利用することができます。
VBO
VBOは、バッファオブジェクトを作成し、バッファをバインドし、頂点情報をVRAMへアップロードすることで利用できます。
※バッファオブジェクトの作成〜アップロードは、前提知識1で説明したシェーダの初期処理に含めて作成します。
□ 頂点バッファ作成
バッファオブジェクトの作成は、「glGenBuffers」を利用します。
glGenBuffersの第2パラメータに、ソースのポインタを渡すことに注意してください。
1 |
glGenBuffers(1, &vertices_buffer); |
□ バインド
バッファを有効にするには、バッファをバインドします。
バッファのバインドには、「glBindBuffer」を利用します。
glBindBufferの第1パラメータには、VBO、IBOを判断するための定数を渡します。
・VBOの場合:GL_ARRAY_BUFFER
・IBOの場合:GL_ELEMENT_ARRAY_BUFFER
1 |
glBindBuffer(GL_ARRAY_BUFFER, vertices_buffer); |
□ バッファデータのアップロード
バッファをバインドすることで、VRAMへ頂点情報をアップロードできます。
アップロードには、「glBufferData」を利用します。
glBufferDataの第2引数は、アップロードするデータのバイト数です。オブジェクトのサイズとオブジェクト数を掛けた結果になります。
第3引数には、アップロードするデータのポイントを設定します。
第4引数には、データの利用方法を指定します。GL_STATIC_DRAWは、一度だけアップロードしますが、アップロードしたデータは利用制限なしで再利用できます。
1 |
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData) * VertexCount, &vertrices, GL_STATIC_DRAW); |
□ バインド解除
VRAMへアップロードした後、バインドを解除します。
バインドの解除には、「glBindBuffer」を利用します。
バインドと同じですが、第2引数に0を設定します。0を設定することで、バインドが解除されます。
1 |
glBindBuffer(GL_ARRAY_BUFFER, 0); |
IBO
基本的にバッファオブジェクトと同じです。
異なるのは、それぞれの第1引数がIBOの設定であるGL_ELEMENT_ARRAY_BUFFERになるところです。
1 |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer); |
※インデックスバッファの作成、バインド、バッファデータのアップロード、バインド解除は、バッファオブジェクトと同じです。
描画
VRAMへアップロードしたバッファオブジェクトを利用し、描画するための処理を解説します。
VBOを用いた描画は、以下の流れになります。
1. 頂点情報の設定
1.1. 頂点情報のデータ準備
1.2. 頂点シェーダ内のデータアクセス許可
1.3. 頂点情報の利用箇所指定
2. フラグメント情報の設定
2.1. フラグメント情報のデータ準備
2.2. フラグメントシェーダ内のデータアクセス許可
2.3. フラグメント情報の利用箇所指定
3. 描画
1. 頂点情報の設定
1.1. 頂点情報のデータ準備
シェーダ内の属性に対して動的に値を設定するためには、外部からシェーダ内の変数にアクセスできるように設定する必要があります。
外部からのアクセスさせるためのデータ準備には、「glGetAttribLocation」を利用します。
1 |
GLint attr_pos_value = glGetAttribLocation(program, "attr_pos"); |
1.2. 頂点シェーダ内のデータアクセス許可
シェーダ内のデータへアクセスするには、「glEnableVertexAttribArray」を利用します。
これにより、頂点シェーダが保持する「attr_pos」変数に外部からアクセスできるようになります。
1 2 3 4 |
attribute mediump vec4 attr_pos; void main() { gl_Position = attr_pos; } |
1 |
GLint attr_pos_value = glGetAttribLocation(program, "attr_pos"); |
1 |
glEnableVertexAttribArray(attr_pos_value); |
1.3. 頂点情報の利用箇所指定
はじめにバッファを再バインドします。次に、頂点情報の利用箇所を指定します。
バッファをバインドしなければ、VRAM上にアップロードしたバッファを利用することはできないので注意してください。
1 |
glBindBuffer(GL_ARRAY_BUFFER, vertices_buffer); |
バッファをバインドしていることを前提にした「glVertexAttribPointer」の設定について解説します。
OpenGLは、この利用箇所の指定を元に描画するためのアクセス領域を確定させます。
頂点情報の利用箇所の指定は、先ほど説明したシェーダの「attr_pos_value」変数に対して、バッファの利用箇所を指定します。
glVertexAttribPointerの引数は、以下の通りです。ポイントは、いずれもVBOを利用している点です。
第1引数:頂点シェーダの属性と関連付けられたインデックス
第2引数:バインドしたVBOの頂点の要素数(今回は、x座標、y座標、z座標の3要素)
第3引数:バインドしたVBOの頂点情報の型
第4引数:正規化有無(GL_FALSE:正規化しない)
第5引数:バインドしたVBOのサイズ
第6引数:バインドしたVBOの先頭のオフセット
1 |
glVertexAttribPointer(attr_pos_value, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid *) 0); |
1 2 3 4 5 6 7 8 |
typedef struct { GLfloat x; GLfloat y; GLfloat z; GLfloat u; GLfloat v; } VertexData; |
2. フラグメント情報の設定
フラグメント情報の設定は、基本的に「頂点情報の設定」と同じです。
2.1. フラグメント情報のデータ準備
フラグメント情報のデータ準備も頂点情報と同様です。「attr_color」に注目してください。
2.2. フラグメント情報へのアクセス許可
フラグメント情報へのアクセス許可も頂点情報と同様です。「attr_color」に注目してください。
1 2 3 4 5 6 7 |
attribute mediump vec4 attr_pos; // 頂点 attribute lowp vec4 attr_color; // カラー varying lowp vec4 vary_color; // フラグメントシェーダーのカラーに連携する void main() { gl_Position = attr_pos; vary_color = attr_color; } |
1 2 3 4 |
varying lowp vec4 vary_color; void main() { gl_FragColor = vary_color; } |
1 |
GLint attr_color_value = glGetAttribLocation(program, "attr_color"); |
1 |
glEnableVertexAttribArray(attr_color_value); |
2.3. フラグメント情報の利用箇所指定
glVertexAttribPointerの第6引数には、バインドしたVBOの先頭のオフセットを指定します。
ここではVertexDataの最初が頂点情報のため、その次からカラーデータであることを指定しています。
1 |
glVertexAttribPointer(attr_color_value, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid *) (sizeof(float)*3)); |
1 2 3 4 5 6 7 8 |
typedef struct { GLfloat x; GLfloat y; GLfloat z; GLfloat u; GLfloat v; } VertexData; |
3. 描画
バッファを利用した描画は、「glDrawElements」を利用します。
「glDrawElements」を利用する前に、バッファをバインドしければなりません。
頂点情報、フラグメント情報の設定には、VBOを指定しました。
描画は、VBOで設定した情報をIBOを利用して描画します。
IBOには、GL_ELEMENT_ARRAY_BUFFERを指定します。
1 |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer); |
次は、描画の設定です。
glDrawElementsの引数は、以下の通りです。ポイントは、いずれもIBOを利用している点です。
第1引数:レンダリングモード。GL_TRIANGLE_STRIPは、連続した三角形を描画するモードです。
第2引数:バインドしたIBOの頂点情報の数
第3引数:バインドしたIBOの型
第4引数:バインドしたIBOの先頭のオフセット
1 |
glDrawElements(GL_TRIANGLE_STRIP, IndexCount, GL_UNSIGNED_INT, (GLvoid *) 0); |
上記は、頂点情報を用いて連続した三角形を描きます。
連続した三角形は、頂点情報の数が多くなればなるほど滑らかなになり、逆に頂点情報が少なければ少ないほど角が目立つようになります。
これにより、効率的に描画できます。
以上で、効率的な描画処理の解説は終わりです。
バックナンバー
OpenGL ES2.0で波紋を作る#2 前提知識2