オブジェクトをスクリーンに描画するためには、グラフィックス API (OpenGL や Direct3D 等) に対してドローコールが必要です。グラフィックス API はドローコールごとに非常に多くの処理をするため、ドローコールは高負荷で、CPU 側のパフォーマンスオーバーヘッドの原因になります。これは大抵、ドローコール間に状況の変更 (例えばマテリアルの切り替えなど) が発生することに起因し、グラフィックスドライバーで高負荷な検証と 変換処理の原因となります。
Unity では、それに対処するために 2 つのテクニックが使用されます。
手動でゲームオブジェクトをマージするのに比べ、ビルトインのバッチにはいくつか利点があります。特に、ゲームオブジェクトは依然として個々にカリングされることが可能です。ただし、それには、欠点もあります。静的バッチでは、メモリとストレージのオーバーヘッドを招きます。そして、動的バッチでは、CPU オーバーヘッドがいくらか発生します。
まとめてバッチ処理できるのは、同じマテリアルを共有しているゲームオブジェクトに限られます。そのため、より効果的にバッチ処理するには、できる限り多くの異なるオブジェクト間でマテリアルを共有するようにします。
もし、テクスチャだけが違う 2 つの同じマテリアルがある場合、これらのテクスチャを合わせて 1つの大きなテクスチャにできます。この処理は、しばしばテクスチャのアトラス化と呼ばれます (Wikipedia の Texture atlases 参照)。いったんテクスチャを同じアトラスにまとめると、1 つのマテリアルとして使うことができます。
共有しているマテリアルプロパティーにスクリプトを通してアクセスする必要がある場合は、以下に注意してください。Renderer.material を変更するとマテリアルの複製を作成します。そうでなく、マテリアルを共有したいのであれば、Renderer.sharedMaterial を使います。
シャドウキャスターは、そのマテリアルが異なっていても、レンダリングのときに一緒にバッチ処理を行う事ができます。Unity のシャドウキャスターは、シャドウパスに必要なマテリアルの値が同じである限り、マテリアルが異なっていても動的なバッチ処理を行う事が可能です。例えば、たくさんの木箱には、異なるテクスチャを持つ複数のマテリアルを使用することもあるでしょうが、シャドウキャスターのレンダリングにはテクスチャは関係ありません。そのため、まとめてバッチ処理されます。
ゲームオブジェクトが同じマテリアルを共有し他の条件を満たす場合、動的なゲームオブジェクトは自動的に 1 つのドローコールにバッチ処理されます。動的なバッチ処理は自動的に行われるので、追加の操作は必要ありません。
動的バッチ処理は、CPU 上でゲームオブジェクトのすべての頂点をワールド空間に変換することによって機能します。そのため、動的バッチ処理の処理負荷がドローコールよりも小さい場合にのみ利点があります。ドローコールが実際どの程度の負荷になるかは、多くの要素によりますが、主に、使われているグラフィックス API によるところが大きいと言えます。例えば、コンソールや Apple の Metal のような最新の API では通常、ドローコールの処理負荷がかなり低く、動的バッチ処理を使用する利点が全くないことがしばしばあります。
Unity によって動的に生成されるジオメトリを持つコンポーネントの場合、動的なバッチ処理はメッシュの場合と異なります。
グラフィックスデバイスの呼び出しの負荷を計る場合、コンポーネントのレンダリングの最も遅い部分はマテリアルの設定部分です。ですから、共有する頂点バッファーに異なるオフセットでドローコールを送信すると非常に高速です。
このアプローチは、静的なバッチ処理を使用するとき Unity がドローコールを送信する方法によく似ています。
静的バッチ処理では、静止していて同じマテリアルを共有するジオメトリであれば、どんなサイズのものにでもドローコールを減らすことができます。静的バッチ処理は CPU 上で頂点の変換を行わないため動的バッチ処理よりも低負荷ですが、メモリを多く消費します。
静的バッチを利用するには、そのオブジェクトが静的で、ゲーム内で移動、回転、スケールを行わないということを明示的に設定する必要があります。そのためには、 インスペクターの Static チェックボックスをオンにします。
静的なバッチ処理を行うには、合成したジオメトリを保存するために余分なメモリが必要になります。静的バッチ処理の前に、複数のゲームオブジェクトが同じジオメトリを共有している場合は、エディター上かランタイムのいずれかで各ゲームオブジェクトのジオメトリのコピーが生成されます。これは、必ずしも良い方法とは言えません。なぜなら、場合によっては、メモリ使用量を小さく維持するために、静的バッチ処理を避けて、レンダリングのパフォーマンスを犠牲にしなければならない場合があるからです。例えば、密集した森林において木を静的にすると、かなりのメモリを消費します。
内部的には、静的バッチ処理は、静的オブジェクトをワールド空間に変換し、それらに共有する頂点とインデックスのバッファーを作成することによって可能です。Player Settings で Optimized Mesh Data を有効にしている場合、頂点バッファーを構築するときにシェーダーバリアントによって使用されていない頂点要素はすべて削除されます。 これを実行するための特別なキーワードチェックがいくつかあります。例えば、Unity が LIGHTMAP_ON キーワードを検出しない場合、ライトマップの UV をバッチから削除します。その後、同じバッチ内の可視のゲームオブジェクトのために、状態の変化はほぼなしで一連の単純なドローコールが実行されます。技術的には、Unity はAPI ドローコール分を節約するのではなく、それらの状態の変化 (これが、負荷の高い部分) 分を節約します。バッチ制限は、ほとんどのプラットフォームでは 64k の頂点と 64k のインデックスです (OpenGLES では 48k のインデックス、macOS では 32k のインデックス) 。
現在、メッシュレンダラー、トレイルレンダラー、ラインレンダラー、パーティクルシステム、スプライトレンダラー だけがバッチ処理可能です。つまり、スキンしたメッシュ、クロス、その他のタイプのレンダリングコンポーネントはバッチ処理できません。
レンダラーは同じタイプのレンダラーとしかバッチ処理できません。
半透明シェーダーでは通常、透明度を出すためにオブジェクトを後方から前方の順番でレンダリングする必要があります。Unity は最初に、この描画順をオブジェクトに指示し、その後、それらをバッチ処理します。しかし描画順を厳密に適用する必要があるので、大抵の場合、不透明オブジェクトほど多くのバッチ処理はできません。
互いに接近しているオブジェクトを手動で組み合わせるのも、ドローコールのバッチ処理にはとても効果的な方法です。例えば、引き出しがたくさんある静的な食器棚は、 3D モデリングアプリケーションか Mesh.CombineMeshes を使って、単一のメッシュにまとめることは理にかなった方法といえるでしょう。
2017–10–26 修正されたページ
グラフィックスジョブと互換性のない動的なバッチ処理に関するノートは 2017.2に追加