高速スプライト表示マジックのタネ

個別のスプライトを大きなテクスチャ1枚にまとめて、そこからテクスチャを貼り付けることで高速化できる。

なぜ速いか?

  • Cocos2DxやUnityではアトラスと呼ばれていて今では普通にみんなやってるテク
  • OpenGLでもDirectXでも、画面にスプライトを表示するには「描画コマンド」というものを発行する
1.テクスチャをバインドせよ(いまからこのテクスチャを使うよ)
2.ポリゴンを描画せよ1
3.テクスチャをバインドせよ(いまから新しいテクスチャを使うよ)
4.ポリゴンを描画せよ2
5.テクスチャをバインドせよ(いまから新しいテクスチャを使うよ)
6.ポリゴンを描画せよ3

この時のテクスチャのバインドに非常に時間がかかる。つまりポリゴン毎にテクスチャを指定するときテクスチャのバインド、ポリゴン描画を繰り返すので、描画コストは6となる。

テクスチャ3枚を大きな1枚にすることで、この描画時の処理負荷を減らすことができる。たとえばこんな感じで描画コストは4にすることができる

  • 1.テクスチャをバインドせよ(いまからこのテクスチャを使うよ)
  • 2.ポリゴンを描画せよ1
  • 3.ポリゴンを描画せよ2
  • 4.ポリゴンを描画せよ3
  • ここでは2しか描画コストは変わらないが、たとえば以下の様な場合を考えてみる

背景の描画

  • 320x320の画面に16x16の背景スプライトを敷き詰めるとする
  • 20x20個の背景スプライトを描く必要がある
    この場合、毎回異なるテクスチャから読み込んできた場合,2x20x20=800のコストとなるが
    同一テクスチャの場合1 + 20x20 = 401となり、約半分のコストにまで下げることができる

気をつけねばならないこと

大きなテクスチャをバインドするときには小さいテクスチャをバインドするよりも時間がかかる
最悪の場合、2倍のテクスチャサイズだった場合2倍かかることもありうる(ハードウェアにもよるっぽい)
  • その場合は、同じコストとなってしまうが、いろいろやってみた結果、1枚にまとめたほうが圧倒的に処理速度が早かった
  • OpenGLのほうがバインドの時間が短いが、ハード(ビデオカード)によるコストのかかり方に幅がある [#f793cda2]
  • DirectXの方は等しくOpenGLより遅いが、ハードウェアによって極端な違いはなかった [#lc2a8232]
  • 動的に変化するテクスチャとかを毎フレームバインドするような処理をするときはOpenGLのほうが都合がよかった [#ub19f862]
  • ポリゴン描画についてはビデオチップがDirectXに最適化されている側面が強いらしく、全体的な処理速度としてはDirectXの方が速かった [#c480cde5]
  • PCにより正しく表示できたり出来なかったりといった機種依存の問題はOpenGLでは度々見られた [#i861200c]

Zソートをさらに高速化する

gh_spd01.PNG
画像の「Order」とは現在描画リクエストを行っている図形(スプライト含む)の数
「Sub」とは同一の図形描画のリクエスト数を表してる

Subに含まれるものの条件は以下。

  • 前回と同じ図形タイプである
  • 前回と同じテクスチャを使っている
  • 前回と同じプライオリティである
  • 前回と同じアトリビュートである

この条件は、途中でレンダリング用のコマンドが変わらないことを示している。
この条件に当てはめて数をかぞえてみると、だいたい70%くらいがSubに含まれる。
本当は2247個の図形に対して2247個分のZソートを行って描画することになるが
Subに含まれているものはプライオリティが同じなので274個(2247-1973)個のオブジェクトに対してのみZソートをすればいいことになる。(厳密にはもう少し多くなるが)

もともとライブラリ側では同じリクエストが来た時にそのレンダリングコマンドを排除する仕様になっているので、描画時の負担はそんなに変わらないがCPUパワーでソートしている部分が少なくなると効率がいい。

Cocos2Dxなんかのスプライトバッチの特性(Zが制御出来ないが高速)をみていると、もしかしたらポリゴン描画時のレンダリングコマンドを最小化したものとしては同じやり方なのかもしれない。

Zソートの高速化はポリゴンモデルに有効か?

いまのライブラリでソフトでポリゴンモデルの描画を行う場合、40000ポリゴン程度のものであっても、テクスチャ付き三角形の描画リクエストを40000個発行することになる。当然Zソートでもこの40000個は計算対象に入ってしまうため負荷は膨大だった。

ポリゴンモデルはモデルどうしてめり込まれるとそれはそれで厄介だし、そもそも2D用のライブラリで3Dゲームを作るのはナンセンスなのであるが、部分的にポリゴンに頼りたい時もある。そこで、自動的に前回の図形と同じものはZソートに含めず、同一プライオリティで描画してしまえばZソートのオーダーに含めずに描画できてしまう。

本当の意味での2.5Dの完成

3Dゲームを2D座標に割り当てるのではなくスプライトの代わりに3Dモデルを動かすというのが技術的に完全に2Dのプライオリティに支配された3Dモデル表示となるため、これはこれでよだれがでる。つまりどんだけ立体的に描画しても、2Dの土俵におけるその3Dモデルはペラペラなのである。これはいい。完全に2Dゲームのプレイフィールで3Dモデルを表示できるのがいい。

横スクロールにおける、面倒な戦車の砲塔回転なんかで威力を発揮できそう。

こりゃすげえ速いよ!

調子にのってコマンドを7万個くらい発光したアタリかrた表示がバグりだした。
そういえばインデックスバッファとか頂点バッファって65535個が限界だったような。。。
同じプリミティブの描画で再帰的に関数を読んでいたらスタックがオーバーフローした。