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