Perfumeニューアルバム「トライアングル」発売記念エントリー!
トライアングルと言えばOpenGLと無理矢理こじつけて、スタンフォード大学の講義でngmoco:)の中の人がOpenGLの高速化テクニックを語っていたので、その内容を自分なりにまとめてみました。
といっても、僕は講義のビデオを見ても英語で何を言ってるのか全然分からないので、あくまでpdfとコードを見て勝手に理解しているだけです。日本語の資料がほとんどないプログラミングの世界にもなれてきて、ソースさえあれば大体分かりますしね。
自分でプログラミングしていくうちに経験則で分かっていた事も含まれていましたが、やっぱりプロは徹底的にやっているなぁという感じがします。半年前に知っておけばBigStopWatchつくるのに役立ったのに...。
講義のpdfとサンプルソースは以下のところにありますので、参考にしてください。AppleのiPhoneDevCenterにも最近テクスチャアトラスのサンプルが加えられましたので、そちらも見ておくと良いと思います。
スタンフォード大学のページ
Lecture 16の下のFriday Section - OpenGLです
ngmocoのFireworksデモのソースのありか
僕は100%独学ですので用語の使い方がおかしいかもしれませんがご容赦ください。あと、すでにOpenGLがある程度使える人が対象です。では、いきます!
テクスチャを使い回すべし
いわゆるテクスチャアトラスってのがかなり効果的です。AppleのOpenGLのドキュメントにもテクスチャアトラスって書いてあって、OpenGLを調べ始めた最初は何の事かよくわからなかったのですが、これはなんかいろいろ試しているうちに分かってきてました。
ふつうにパフォーマンスを気にせずテクスチャを描画する場合、表示する画像ごとにテクスチャを作って、glRotateとかglTranslateとかでジオメトリを変換して、glDrawArrays(glDrawElements)で描画してってのを繰り返して描画していくって感じでしょうが、ちょっと数が増えるとあっという間にカクカクっとパフォーマンスが落ちていきます。
glBindTextureでテクスチャを切り替えたりするところが特に時間がかかって描画のパフォーマンスが落ちてしまうところなので、必要な画像は全て一枚のテクスチャにいれておいて、その頂点を並べたでっかい配列を作り、一回のglDrawArraysですませて回避します。
テクスチャを複数使う場合も、あっちのテクスチャこっちのテクスチャみたくやたらめったら切り替えるのではなく、同じテクスチャで描画出来るものはまとめて描画した後、別のテクスチャを描画するってした方が速いです。とにかくglBindTextureを減らします。
2Dの頂点を自前で変換していくのにはCGAffineTransformが役に立つと思います。3DのはiPhoneだと無いみたいです。
配列をひとつにまとめるべし
さらにngmoco:)さんはIndexed Arrayといって、glVertexPointerとglTexCoordPointerとglColorPointerで使う1頂点のデータを構造体で作って配列に並べ、構造体のサイズ分ストライドで飛ばしつつ使うという方法をやっています。そうするとメモリの効率も良いようです。
ステートは変更するべからず
glBindTextureの他にも、glColorとかglEnableも出来るだけ使わない方が良いとのこと。
頂点を減らすべし
それをいっちゃ当たり前なんですが、描画する頂点が少なければそれだけパフォーマンスが上がるってことです。
ngmocoの今回のサンプルでは四角の画像を使っているので、TRIANGLES二つずつ使ってたくさん貼付けていくって言うパターンでこれ以上やりようがないみたいですが、場合によってTRIANGLE_STRIPをうまく使えれば、頂点数を減らせるという感じでしょうか。
フロートは速し、でもショートはもっと速し
だそうです。glVertexPointer用の頂点配列に書き込むときにfloatからあえてshortにしてますね。でもドット単位で小数点以下の位置が切り捨てられてしまうので、スムーズに動かしたい場合はやっぱりfloatですねぇ。
テクスチャはPVRTCを使うべし
僕はテクスチャの画像をプログラム上で起動時に描画して作っていたりするので試したことは無いのですが、PVRTCという圧縮された画像ファイルをテクスチャに読み込んで使った方が速いとか。iPhoneに最適化されているってことでしょうかね。そういえば去年の10月にも聞いたような気がする...。あのころはOpenGLなんて無縁だと思っていたなぁ。
glTexSubImage2Dは描画中に使わざるべからず
みたいな事がアップルのドキュメントに書いてあったりするんですが、僕はTouch the Waveで使いまくってます。つかわなくちゃアプリが成り立たなかったりするんで。
気をつけなくちゃいけないのは、部分的にテクスチャを差し替えるだけでも、テクスチャ全体の大きさに比例して処理する時間が長くなってしまうという事です。1024x1024のテクスチャを書き換えようとしたらハンパないくらい処理が止まります。テクスチャ一つを使い回すのは事前にロードできる場合の話で、描画中に書き換える場合は出来るだけ最小限の大きさのテクスチャをバラバラに作った方がよいです。
とまぁ、こんなところかと思いますが、やりたいことによって高速化できる部分と出来ない部分もあったりとかしますので、臨機応変に速い手段を取捨選択していければよいかと思います。
ちなみに、BigStopWatchを全て一回のglDrawArraysでやってみようと思って試したら、描画一回分の計算に時間がかかりすぎたみたいで、逆にパフォーマンスが落ちちゃいました。少なくともBigStopWatchでは、Fireworksデモみたいに毎フレーム頂点位置を計算するのではなく、「時間の文字列」とか「目盛り」とか頂点の位置が固定できるような分け方をした配列を前もって作っておいて、glRotateとかやってglDrawArraysを繰り返していった方が良いみたいでした。
コメントする