Element Array Bufferによる描画を行う

# はじめに
* Element Array Bufferを用いて、図形の描画を解説する。



## 解説用ソースコード
　以下のソースコードを元に、解説を進める。
 [GitHub - 02-render_with_ebo](https://github.com/yumataesu/ModernOpenGL-AdventCal2017/tree/master/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**
 

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

**EBOあり 結果:216byte**

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


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

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

![スクリーンショット 2017-11-29 19.43.34 2.png](https://qiita-image-store.s3.amazonaws.com/0/104234/ebba6d8c-1d63-0c73-207c-a899deb3b873.png)
*[OpenGL 3D Game Tutorial 3: Rendering with Index Buffers by thin Matrix](https://www.youtube.com/watch?v=z2yFlvkBbmk)*



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



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

```C++
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の和訳を載せているので、気になる人は参照してほしい。

```C++
glBindVertexArray( vao );
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0 );
glBindVertexArray( 0 );
```

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

## oFでもEBOはofVbo

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


```C++
// 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](https://qiita-image-store.s3.amazonaws.com/0/104234/e8e67e75-294e-ca27-57f3-6c4494adb23c.gif)



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



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




### 余談2. glDrawElements()について

　ほとんど、[公式ドキュメント](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElements.xhtml)の和訳だが、以下に仕様を載せておく。

```
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にしている。