オブジェクトをスクリーンに描画するために、エンジンはグラフィック API( OpenGL や Direct3D 等)に対してドローコールを発行しなければいけません。グラフィック API ではドローコールごとに重要な役割が果たされることから、ドローコールは高価で、CPU 側のパフォーマンスオーバーヘッドの原因になります。よくある原因は、ドローコール間でのステートチェンジ(例えばマテリアルの変更など)であり、これがグラフィックドライバでの validation と translation のステップが高価になってしまう原因となります。
Unity では、それに対処するためにいくつかのテクニックが使用されます。
手動でオブジェクトを統合するのに比べ、ビルトインのバッチにはいくつかの利点があります (もっとも注目するのは、オブジェクトがまだ、個別に抜粋できることです)。しかし、いくつかの欠点もあります (静的バッチはメモリーとストレージのオーバーヘッドを生じさせ、動的バッチはいくつかの CPU オーバーヘッドを生じさせます)。
同じマテリアルを共有しているオブジェクトがバッチング可能になります。そのため、より効果的なバッチングをするには、できる限り違うオブジェクト間で多くのマテリアルを共有する必要があります。
もし二つのテクスチャが違うだけの独立したマテリアルがあるのであれば、二つのテクスチャを組み合わせて一つのテクスチャに合成しましょう( テクスチャアトラス と呼ばれるプロセスです)。いったんテクスチャを同じアトラスにしてしまえば、単一のマテリアルとして使うことができるようになります。
もしスクリプトから共有しているマテリアルプロパティーをアクセスするならば、以下の情報が重要です: Renderer.material の変更はマテリアルのコピーが生じます。共有しているマテリアルをキープしたいのであれば、代わりに Renderer.sharedMaterial を使いましょう。
シャドウキャストのレンダリングにおいては、仮にそれらのマテリアルが異なっている場合でも、バッチングする事ができます。 Unity のシャドウキャストは、マテリアルが異なっていても、シャドウパスが必要とするマテリアルの値が同じであれば、ダイナミックバッチングを利用する事が可能なのです。例えば、沢山の木枠のマテリアルではそれぞれ異なるテクスチャを使うことができますが、シャドウキャスターはテクスチャは関係ないものとしてレンダリングします。このような場合、互いにバッチングすることが可能です。
同じマテリアルを共有していて他の条件を満たせば、Unity は自動的に動いているオブジェクトをバッチングします。ダイナミックバッチングは自動的に処理されるので、何かの手間が必要というわけではありません。
CPU 上ではオブジェクトの頂点は全てワールドスペース座標で動いているため、 “ドローコール” よりも処理負荷が小さい場合のみ、有効となります。ドローコールが実際どの程度の負荷になるかは、多くの要素に依存しますが、使われているグラフィックス API によるところが大きいです。例えば、コンソールやアップルの Metal のような現代的な API では通常、ドローコールの処理負荷がかなり低く、ダイナミックバッチングは全く効果的ではありません。
一方でスタティックバッチングはどんなサイズのジオメトリでもドローコールを減らすことができます(動かなくて同じマテリアルを共有していることが条件です)。スタティックバッチングはダイナミックバッチングよりも顕著に効果的(CPU上で頂点変換を行わないため)ですが、よりメモリを消費します。
スタティックバッチングを利用するには、そのオブジェクトがゲーム中、動いたり、回転したり、スケールしたりすることが ない ということを明示的に設定する必要があります。そのためには、 Inspector の Static チェックボックスをオンにします:
スタティックバッチングを使うと、合成したジオメトリ情報を保存しておくための余分なメモリが必要になります。スタティックバッチングの前に、複数のオブジェクトで同じジオメトリを共有しているのであれば、オブジェクト毎にそのジオメトリのコピーが生成されます(エディター上でもランタイムでも)。このため、いつでも有効な方法とは言えません。つまり場合によっては、メモリ使用量を小さいまま維持するために、スタティックバッチングを避けて、レンダリングのパフォーマンスを犠牲にしなければならないこともあるでしょう。例えば、密集した森林において、木をスタティックにすると、メモリにはかなりの負担になるかもしれません。
内部的には、スタティックバッチングは各スタティックオブジェクトの位置座標をワールドスペースに変換し、それらから頂点+インデックスの巨大なバッファを作成しています。その後、同じバッチ内にある可視物体については、ステートの変化がない場合、一連の“安価”なドローコールが割り当てられます。技術的に言うと“3D API draw calls”は節約できませんが、それら物体間で発生するステート変化は節約されます(これこそが計算量の多い部分です)。
現在は、メッシュレンダラー のみがバッチングされます。すなわち、スキンメッシュ、クロス、トレイルレンダラーおよびその他の種類のレンダリングコンポーネントはバッチング されません 。
半透明シェーダーでは大抵、透明度を正しくするために、オブジェクトを後方から前方の順番でレンダリングする必要があります。Unity は最初に、この描画順をオブジェクトに指示し、その後、バッチングを試みます - しかし描画順を厳密に適用しなければいけないので、大抵の場合、不透明オブジェクトに比べるとバッチングが少なくなります。
互いに接近しているオブジェクトを手動で合成するのも、ドローコールバッチングにはとても効果的な、また一つの方法です。例えば、引き出しがたくさんある静的な戸棚は、 3D モデリングアプリケーションか Mesh.CombineMeshes を使って、単一のメッシュにまとめてしまう事がよくあります。