テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

色を変えるがドローコールを増やさない場合

本稿は古い記事です。
現在の最適解はこちら
tsubakit1.hateblo.jp



意外と知られているハズなのに皆が何故か引っかかる罠の一つに、マテリアルの値を変えるとマテリアルが複製されてどローコールが増えるがある。

これはShardMaterialの存在を思えば単純な話で、多分内部的にこんな感じで動いてる。

  1. 特定マテリアルの色を変更するよう指示
  2. 共通マテリアルは変更しないように、共通マテリアルからマテリアルをコピーしてインスタンス
  3. コピーしたマテリアルを変更(どローコールが増える)

で、このコピーしたマテリアルの分だけ描画が必要になるのでドローコールが増えるという訳。ちなみに、一度インスタンス化したマテリアルは以降値を変更してもドローコールは増えない。

このドローコール増加対策は簡単で、概ね3つの方法がある。

  1. renderer.sharedMaterialを使用する
  2. Materialをある程度キャッシュしておく
  3. メッシュの頂点カラーを変更する

1についてだが、renderer.sharedMaterialは共通マテリアルにアクセスする。つまり共通マテリアルを更新するのでマテリアルをインスタンス化する必要がなくなるということ。但し、これは同一マテリアルを参照している全オブジェクトの色が変わる。

2は若干面倒だが効率的。例えば作ったマテリアルをマテリアル名・色・適応メッシュ毎にDictionaryに保存しておき、必要な際に検索して配置する。これなら若干管理が面倒だが、使用色の分だけのドローコールで済む。ただDictionaryのキーを単純にしないと検索でCPUを超喰うので注意(例えばColorをキーにするとか)

3は要するにメッシュを更新する。但し、シェーダーが頂点カラーに対応していないと色が変わらないので注意。こっちはマテリアルには一切手を付けないので、ドローコールは増えない。
ちなみにMeshは元々動かさないこと前提で作られているので、Mesh.MarkDynamicで頻繁に更新できるメッシュとしておくべきらしい。

スクリーンショット 2013-10-20 16.27.06
9色使っているがドローコールは1

参考:Unity デフォルトのオブジェクトに頂点カラーを追加する

どれが良いかと言われると難しい。
最速は間違いなくsharedMaterialの変更だろうが、他のマテリアルも同様に色が変わってしまうので使い所が難しい。

次点として効率的なのはマテリアルキャッシュだが、これはマテリアルを細分化すればするほど効率が落ちる。これはマテリアルキャッシュへアクセスするCPU負荷とドローコールの負荷とのバランスとなる。またスケールが異なるモデルに適応してもバッチングが効かなければ意味が無い。

頂点カラーの方法は悪くはないが、実はそこまで高速ではない。ドローコールを放置して増やすよりは高速だが、思いの外パフォーマンスが出なかった。

あと、正直ドローコールなんぞよりフィルレートの方が今の環境だと問題な気がする。


ちなみに、こっちは余り知られていないかもしれないが、マテリアルに値を突っ込んだ際にインスタンス化したマテリアルは、親オブジェクトをDestroyしても破棄されない

一応Resources.UnloadUnusedAssetsで破棄されるが、これが数万・数十万となると若干スパイクが大きくなるかもしれないので注意。(弾幕シューティングで色が常時変わるようなものを使う場合とかね!)