原始人のプログラミング日記

println(++C++); // println(C++), ++C;

.objとIBO

.objのフォーマット

.objファイルは主に頂点座標とテクスチャ座標、法線の3つの情報から構成されていて、それを表面情報でインデックス付けしている。
これによって同じ座標の頂点はひとまとめにされ、同様にテクスチャ座標なんかも一つにまとめられて扱いやすくなっている。

IBOとの親和性

フォーマット的にインデックス付けされてるし、一番最初にIBOを使おうと考えるんだけど、結論から言えばOpenGLでは無理っぽい。

無理とは言ったけど、たとえば頂点だけを利用して描画する分には可能。
この場合、法線は一方向のみでテクスチャなんかも使えない。

でもせっかく作ったのだから、法線で陰影付けしたいしテクスチャだって貼り付けたい。
この場合、.objでは一つの頂点に対して3つの属性(頂点/テクスチャ/法線)が付与されていて、それぞれが別のインデックスで表されているため一つのIBOでは対応できない。
それならということで、頂点のIBO、テクスチャのIBO、法線のIBOをそれぞれ設定することを考えるがOpenGLではこれができないみたい(OpenGLでは複数のIBOを使用できないらしい*1 )。
つまり、同じ頂点(ここでの同じとは3属性すべてがおなじであること)が限りなく少ない.objファイルではIBOの仕組みを使うことに意味がないということになる。

.objを展開

IBOが使えないということで、頂点情報をインデックスではなく実際の値として展開する必要がでてくる。
つまり

f 3/8/9

v 0.00 -0.44 -1.05/vt 0.09 0.72/vn 0.00 -0.97 0.21

の形式に展開しなければならない。

このとき、モデルのポリゴン数が仮に4000ポリゴンだとすると頂点数は3倍の12000でかなり多い(純粋な頂点数はカエルの場合8000弱)。
メモリの消費が激しいこの方法だが、調べた限りでは展開することが一般的なようだ(展開以外のいい手法がない?)。

問題点

上記の通り、ポリゴン毎に頂点を展開していくとメモリを消費することは妥協するしかないにしても、データの変更にかなりのコストがかかるという問題が生じる。
例えば、本来8000頂点に対してだけ行えばよかった演算を、1.5倍の12000頂点に対して行う必要が生じてしまう(回転の場合、さらに法線にも処理を行うので約3倍以上の処理)。

解決策

展開すること自体泥臭かったが、解決策はもっと泥臭く、自分の考えた方法はインデックスを処理毎に展開しなおすことだ。
つまり回転にしろ移動にしろ、処理を行うのは純粋な頂点(重複のない展開前の頂点)だけにし、処理を行った後に随時描画用の頂点に展開しなおすということだ。
一見遅くなりそうなこの方法だが、自分独自のプログロムのフレームレートでは、展開済みの全頂点に対して処理を行う場合の20fpsから設定値の50fpsまでに上昇した。

終わりに

今回書いたのは、自分の調べた範囲の情報と解決法であり、不確かだったり至らない点が多いと考えられ、多分恥をかくような内容かもしれない。
そもそも、こんな冗長な方法しかなくOpenGLに適切な方法が用意されていないのはにわかに信じがたい。
仮にOpenGLに用意されてなくても上記以上にいい方法がある、もしくはOpenGL自体にこの問題を解決するスマートな方法がある場合、是非とも教えて欲しいし知りたいと思う。

終わり

*1:できたら教えてほしい