6
6

More than 5 years have passed since last update.

Element Array Bufferによる描画を行う

Posted at

はじめに

  • Element Array Bufferを用いて、図形の描画を解説する。

解説用ソースコード

 以下のソースコードを元に、解説を進める。
GitHub - 02-render_with_ebo

複雑な図形の描画、無駄な頂点について

 例えば、四角形を描画したい場合、glDrawArray()を用いると三角形が2つ必要になる。頂点数でいえば6つ用意する必要があり、重複している無駄な頂点が2つ存在することになる。これはglDrawArray()が頂点の配列の順番をみて、頂点の結び方を決定する仕様からきている。

Element Array Bufferで、描画に使う頂点を再利用

 上記の問題点は、一つの頂点を一回の描画にしか使えないことだ。
これを解決する手段として、一つの頂点を複数の描画に再利用できる機能が存在する。それが、Element Array Bufferである。

Element Array Buffer
 Element Array Buffer(EBO)は、VBOと同様バッファオブジェクトの一つ。
 頂点にインデックス属性(どのように頂点を結ぶか)を付加することができるバッファである。インデックス属性は、一つの頂点に対して複数付加することができ、これにより描画する際に頂点の再利用が可能になる。

GL_ELEMENT_ARRAY_BUFFERのバインドポイントで生成する。Index Buffer Objectとも呼ばれている。

単純な四角でもメモリ消費を20%減できる。

 頂点を再利用することができるので、メモリの削減やパフォーマンスの向上がメリットといえる。
 下記のように、実際には法線とテクスチャ座標が加わった四角を描画したとする。
(48byteの計算結果は、左から位置、色、法線、テクスチャ座標を表している。)

EBOなし 結果:288byte

3float + 4float + 3float + 2float = 48byte \\
48 \times 6 = 288byte \\

EBOあり 結果:216byte

3float + 4float + 3float + 2float = 48byte \\
6int = 24byte \\
48 \times 4 + 24 = 216byte \\

この四角形だけでも、EBOありの方が20%程の少ないことがわかる

さらに複雑な形状ならば、重複している頂点が膨大になるので、EBOのメリットが出てくるだろう。

スクリーンショット 2017-11-29 19.43.34 2.png
OpenGL 3D Game Tutorial 3: Rendering with Index Buffers by thin Matrix

では、コードを交えつつ実装方法をみていく...

EBOもVBOとほぼ同じ手続きで生成する。

 EBOは、VBOとほぼ同じ手続きで生成する。
glBindVertexArray()中に作成すると、VAOが自動的にEBOとしてバインドしてくれる。

int indices[] = {0, 1, 2, 0, 3, 2};

glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

glDrawElementsで描画する

 描画には、専用のglDrawElements()を用いる。余談の項目に公式docの和訳を載せているので、気になる人は参照してほしい。

glBindVertexArray( vao );
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0 );
glBindVertexArray( 0 );

上記の実装で4つの頂点で四角を描画できた。

oFでもEBOはofVbo

 ofVboは、VBOとVAOの機能を持っていると解説したが、EBOの機能も持っている。
ofVboに、EBOをバインドするには、ofVbo::setIndexData()を用いる。

// Position Attribute
std::array<ofVec3f, 4> of_vertices;
of_vertices[0] = ofVec3f(-0.7f, -0.7f, 0.0f);
of_vertices[1] = ofVec3f(0.7f,-0.7f, 0.0f);
of_vertices[2] = ofVec3f(0.7f, 0.7f, 0.0f);
of_vertices[3] = ofVec3f(-0.7f,0.7f, 0.0f);

// Color Attribute
std::array<ofFloatColor, 4> of_colors;
of_colors[0] = ofFloatColor(1.0f, 0.0f, 0.0f, 1.0f);
of_colors[1] = ofFloatColor(0.0f, 1.0f, 0.0f, 1.0f);
of_colors[2] = ofFloatColor(0.0f, 0.0f, 1.0f, 1.0f);
of_colors[3] = ofFloatColor(0.0f, 1.0f, 1.0f, 1.0f);

// Index Attribute
std::array<ofIndexType, 6> of_indices = {0, 1, 2, 0, 3, 2};


{
    of_vao.bind();
    of_vao.setVertexData(&of_vertices[0], 4 * sizeof(ofVec3f), GL_STATIC_DRAW);
    of_vao.setColorData(&of_colors[0],   4 * sizeof(ofFloatColor), GL_STATIC_DRAW);
    of_vao.setIndexData(&of_indices[0],    6 * sizeof(ofIndexType), GL_STATIC_DRAW);
    of_vao.unbind();
}

キャプチャ

 以下のような四角が描画される。
ebo2.gif

まとめ

  • EBOを用いると、描画にあたり頂点を再利用する
  • EBOによる描画は、glDrawElements()を用いる
  • oFでEBOを利用するには、ofVboを用いる。

余談1. 3Dモデルはあまり気にしなくていい?

 筆者自身あまり詳しくないので断言できないが、世の中の3Dモデルに対して「インデックスをもったデータか」という心配はあまりしなくていいように思う。
 実際、スタンフォードドラゴンを、MeshLabで「Remove Duplicate Vertices」しても、削減はできなかった上、ofxAssimpModelLoaderも、ofVbo::drawElements()しか実装されていない。
そのため、「3Dモデルはインデックスをもってエクスポートされるしできる前提」だと思っていいのではないか。

余談2. glDrawElements()について

 ほとんど、公式ドキュメントの和訳だが、以下に仕様を載せておく。

glDrawElements(GLenum mode,
        GLsizei count,
        GLenum type,
        const GLvoid * indices)
  • mode : 描画するプリミティブの指定。以下が指定可能。

    • GL_POINTS
    • GL_LINE_STRIP
    • GL_LINE_LOOP
    • GL_LINES
    • GL_LINE_STRIP_ADJACENCY
    • GL_LINES_ADJACENCY
    • GL_TRIANGLE_STRIP
    • GL_TRIANGLE_FAN
    • GL_TRIANGLES
    • GL_TRIANGLE_STRIP_ADJACENCY
    • GL_TRIANGLES_ADJACENCY
    • GL_PATCHES
  • count : Indexの数を指定。

  • type : Indexの型を指定。

    • GL_UNSIGNED_BYTE
    • GL_UNSIGNED_SHORT
    • GL_UNSIGNED_INT
  • indices : インデックスが格納されている場所へのポインタを指定。VAOにバインドしているのでここでは、0にしている。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6