
Unity プロジェクトの最適化のヒントを紐解くシリーズの第 3 弾です。より少ないリソースでより高いフレームレートで実行するためのガイドとしてご利用ください。これらのベストプラクティスを試したら、シリーズの他のページもぜひご覧ください。
Unity 6 の開発者とアーティスト向けの最新の最適化ガイドをご覧ください。
ターゲットハードウェアの制限と、グラフィックスのレンダリングを最適化するために GPU をプロファイリングする方法を理解します。GPU のワークロードを減らすためのヒントやベストプラクティスをお試しください。
プロファイリングを行う場合は、特定の GPU からどのようなプロファイリング結果が得られるかを知るために、ベンチマークから始めると便利です。
GPU およびグラフィックスカード用のさまざまな業界標準ベンチマークの一覧については、GFXBench を参照してください。ウェブサイトでは、現在利用可能な GPU の概要と、GPU がどのように競合するかについて説明しています。
レンダリング統計を見る
Game ビューの右上にある Stats ボタンをクリックします。このウィンドウには、再生モード中のアプリケーションに関するリアルタイムのレンダリング情報が表示されます。このデータは、パフォーマンスの最適化に役立ちます。
"FPS"1 秒あたりのフレーム数
- CPU Main:1 フレームの処理(およびすべてのウィンドウのエディターの更新)にかかった合計時間
- CPU Render:ゲームビューの 1 フレームのレンダリングにかかった合計時間
- Batches:一緒に描画されるドローコールのグループ
- 三角形と頂点:メッシュジオメトリ
- SetPass calls:Unity が画面上にゲームオブジェクトをレンダリングするためにシェーダーパスを切り替える回数。パスごとに余分な CPU オーバーヘッドが発生する可能性があります。
注:エディター内の fps がビルドパフォーマンスに必ずしも影響するわけではありません。最も正確な結果を得るために、ビルドをプロファイルすることをお勧めします。ベンチマークでは、ミリ秒単位のフレーム時間が 1 秒あたりのフレーム数よりも正確な指標となります。詳細については、eBook『Ultimate guide to profiling Unity games』(Unity 6版)を参照してください。

ゲームオブジェクトを描画するために、Unity はグラフィックス API(OpenGL、Vulkan、Direct3D など)にドローコールを発行します。各ドローコールはリソースを大量に消費します。マテリアルの切り替えなど、ドローコール間の状態変化は、CPU 側でパフォーマンスのオーバーヘッドを引き起こす可能性があります。
PC やコンソールのハードウェアは多くのドローコールをプッシュできますが、各コールのオーバーヘッドは依然として高く、それらを削減しようとすると保証されません。モバイルデバイスでは、ドローコールの最適化が不可欠です。これは、ドローコールバッチングで実現できます。
ドローコールバッチングは、これらの状態変化を最小限に抑え、オブジェクトのレンダリングの CPU コストを削減します。Unity では、いくつかのテクニックを使用して、複数のオブジェクトをより少ないバッチにまとめることができます。
- SRP Batching:HDRP または URP を使用している場合は、パイプラインアセットの「Advanced」で SRP Batcher を有効にします。互換性のあるシェーダーを使用している場合、SRP Batcher はドローコール間の GPU セットアップを減らし、マテリアルデータを GPU メモリに永続化します。これにより、CPU のレンダリング時間を大幅に短縮できます。シェーダーバリアントを少なくし、最小限のキーワードで SRP バッチングを改善します。プロジェクトでこのレンダリングワークフローを活用する方法については、こちらの SRP ドキュメントを参照してください。
- GPU インスタンシング:同一のオブジェクト(同じメッシュとマテリアルを持つ建物、樹木、草など)が多数ある場合は、GPU インスタンシングを使用します。この手法では、グラフィックスハードウェアを使用してバッチ処理を行います。GPU インスタンシングを有効にするには、Project(プロジェクト)ウィンドウで対象のマテリアルを選択し、インスペクターで Enable Instancing(インスタンシングを有効にする)チェックボックスをオンにします。
- 静的バッチ処理:動かないジオメトリの場合、Unity は同じマテリアルを共有するメッシュのドローコールを減らすことができます。動的バッチングよりも効率的ですが、より多くのメモリを使用します。インスペクターで、動かないすべてのメッシュを「Batching Static」としてマークします。Unity はビルド時にすべての静的メッシュを 1 つの大きなメッシュに統合します。StaticBatchingUtility を使用すると、実行時に(たとえば、プロシージャルレベルの可動部分以外を生成した後に)、これらの静的バッチを自分で作成することもできます。
- ダイナミックバッチング:小さなメッシュの場合、Unity は CPU 上で頂点をグループ化して変換し、一度にすべて描画できます。注:十分な数のローポリゴンメッシュ(各頂点が 300 以下、合計頂点アトリビュートが 900 以下)がない限り、これを使用しないでください。そうしないと、バッチ処理する小さなメッシュを探すのに CPU 時間を浪費してしまいます。
バッチ処理はいくつかの簡単なルールで最大限に行うことができます。
- シーンで使用するテクスチャはできるだけ少なくする。テクスチャの数が少ないほど、必要な固有マテリアルの数も少なくなり、バッチ処理が容易になります。さらに、可能な限りテクスチャアトラスを使用します。
- ライトマップを常に可能な限り大きなアトラスサイズでベイクします。ライトマップの数が少ないほどマテリアルの状態変化も少なくて済みますが、メモリフットプリントに注意してください。
- 意図せずにマテリアルをインスタンス化しないように注意してください。スクリプトで Renderer.material にアクセスすると、マテリアルが複製され、新しいコピーへの参照が返されます。これにより、マテリアルがすでに含まれている既存のバッチが壊れます。バッチ化されたオブジェクトのマテリアルにアクセスする場合は、代わりに Renderer.sharedMaterial を使用します。
- 最適化中にプロファイラーまたはレンダリング統計を使用して、静的および動的バッチ数とドローコール数の合計を常に監視します。
詳細については、ドローコールバッチングのドキュメントを参照してください。

フレームデバッガーを使用すると、1 フレームで再生をフリーズし、Unity がシーンを構築する方法をステップ実行して最適化の機会を特定できます。不必要にレンダリングされるゲームオブジェクトを探し、それらを無効にしてフレームごとのドローコールを減らします。
注:フレームデバッガーには、個別のドローコールや状態変化は表示されません。詳細なドローコールとタイミング情報を与えてくれるのはネイティブの GPU プロファイラーだけです。ただし、フレームデバッガーはパイプラインの問題やバッチ処理の問題のデバッグには引き続き非常に役立ちます。
Unity フレームデバッガーの利点の 1 つは、ドローコールをシーン内の特定のゲームオブジェクトに関連付けできることです。これにより、外部フレームデバッガーでは不可能な特定の問題の調査が容易になります。
詳細については、フレームデバッガーのドキュメントを参照してください。

フィルレートとは、GPU が 1 秒間に画面にレンダリングできるピクセル数のことです。
ゲームがフィルレートによって制限されている場合は、GPU が処理できるよりも多くのピクセルをフレームごとに描画しようとしていることを意味します。
同じピクセルの上に何度も描画することをオーバードローと呼びます。オーバードローはフィルレートを下げ、余分なメモリ帯域幅を消費します。オーバードローの最も一般的な原因は、次のとおりです。
- 不透明または透明なジオメトリのオーバーラップ
- シェーダーが複雑で、多くの場合、複数のレンダーパスが必要
- 最適化されていないパーティクル
- UI 要素の重複
その影響は最小限に抑えたいところですが、オーバードローの問題を解決するための万能のアプローチはありません。上記の要素の影響を小さくするために、まずはこれらの要素を試してみましょう。
他のプラットフォームと同様に、コンソールでの最適化はドローコールのバッチを減らすことを意味します。役に立つテクニックがいくつかあります。
- オクルージョンカリングを使用して、フォアグラウンドオブジェクトの後ろに隠れたオブジェクトを削除し、オーバードローを減らします。これには追加の CPU 処理が必要なので、プロファイラーを使用して GPU から CPU への移行が有益であることを確認してください。
- GPUインスタンシングは、同じメッシュとマテリアルを共有するオブジェクトが多数ある場合に、バッチを減らすこともできます。シーン内のモデル数を制限すると、パフォーマンスが向上します。巧妙に作れば、繰り返し見せることなく複雑なシーンを構築できます。
- SRP Batcher は一連のバインドとドローの GPU コマンドをバッチングすることにより、ドローコール間の GPU 設定を減らします。この SRP バッチングの恩恵を受けるには、必要なだけマテリアルを使用し、互換性のある少数のシェーダー(URP や HDRP の Lit シェーダーや Unlit シェーダーなど)に制限します。
オクルージョンカリングは、他のゲームオブジェクトによって完全に隠されている(オクルージョンされている)ゲームオブジェクトを無効にします。これにより、CPU と GPU がカメラに映らないオブジェクトのレンダリングに時間を費やすことがなくなります。
カリングはカメラごとに発生します。特に複数のカメラを同時に有効にすると、パフォーマンスに大きな影響を与える可能性があります。Unity では、錐台カリングとオクルージョンカリングの 2 種類のカリングを使用しています。
錐台カリングはすべてのカメラで自動的に実行されます。これにより、ビュー錐台の外側にあるゲームオブジェクトがレンダリングされなくなり、パフォーマンスの最適化に役立ちます。
レイヤーごとのカリング距離は、Camera.layerCullDistances から手動で設定できます。これにより、デフォルトの farClipPlane よりも短い距離で小さなゲームオブジェクトをカリングできます。
ゲームオブジェクトをレイヤーに整理します。layerCullDistances 配列を使用して、32 の各レイヤーに farClipPlane より小さい値を割り当てます(または、0 を使用してデフォルトで farClipPlane に設定します)。
Unity はまずレイヤーごとにカリングを行い、カメラが使用するレイヤーにのみゲームオブジェクトを保持します。その後、錐台カリングによってカメラ錐台の外側のゲームオブジェクトが削除されます。錐台カリングは、利用可能なワーカースレッドを利用する一連のジョブとして実行されます。
各レイヤーカリングテストは非常に高速です(基本的にはビットマスク操作のみ)。しかし、このコストはゲームオブジェクトの数が非常に多い場合にも膨らむ可能性があります。これがプロジェクトで問題になった場合は、Unity のレイヤー/錐台カリングシステムにかかる負荷をいくらか軽減するために、ワールドを「セクター」に分割し、カメラ錐台の外側にあるセクターを無効にするシステムを実装する必要があるかもしれません。
オクルージョンカリングは、カメラに映らないゲームオブジェクトをゲームビューから削除します。この機能を使用すると、他のオブジェクトの後ろに隠れたオブジェクトがレンダリングされ、リソースが消費されるのを防ぐことができます。例えば、ドアが閉まっていてカメラが部屋を見渡せない場合は、別の部屋をレンダリングする必要はありません。
オクルージョンカリングを有効にすると、パフォーマンスが大幅に向上しますが、より多くのディスクスペース、CPU 時間、RAM が必要になることもあります。Unity はビルド中にオクルージョンデータをベイクし、シーンのロード中にディスクから RAM にロードする必要があります。
カメラビュー外での錐台カリングは自動的に行われますが、オクルージョンカリングはベイク処理です。オブジェクトを Static.Occluder または Occludees としてマークし、「Window」>「Rendering」>「Occlusion Culling」ダイアログでベイクします。
詳細については、オクルージョンカリングの操作チュートリアルを参照してください。

「Allow Dynamic Resolution」は、個々のレンダーターゲットを動的にスケールして GPU のワークロードを軽減できるカメラ設定です。アプリケーションのフレームレートが低下した場合は、一定のフレームレートを維持するために解像度を徐々に縮小できます。
Unity は、GPU 依存の結果としてフレームレートが低下しようとしていることをパフォーマンスデータから示唆している場合に、このスケーリングをトリガーします。このスケーリングは、スクリプトを使用して手動で事前にトリガーすることもできます。これは、アプリケーションの GPU 負荷の高いセクションに差し掛かる場合に便利です。徐々にスケーリングすれば、動的解像度はほとんど気にならなくなります。
追加情報とサポートされているプラットフォームのリストについては、動的解像度のマニュアルページを参照してください。
ゲーム中に複数の視点からレンダリングする必要がある場合があります。例えば、FPS ゲームでは、有効視野(FOV)を変えてプレイヤーの武器と環境を別々に描くのが一般的です。これにより、背景の広角 FOV を通して前景のオブジェクトが歪んで見えるのを防ぐことができます。
URP でカメラスタッキングを使用すると、複数のカメラビューをレンダリングできます。ただし、カメラごとにカリングとレンダリングがかなり行われます。各カメラには、意味のある仕事をしているかどうかにかかわらず、ある程度のオーバーヘッドが発生します。レンダリングに必要なカメラコンポーネントのみを使用してください。モバイルプラットフォームでは、何もレンダリングしない場合でも、アクティブなカメラはそれぞれ最大 1ms の CPU 時間を消費できます。

URP では、複数のカメラを使用する代わりに、カスタムの Render Objects Renderer Feature を試してみてください。Renderer Data アセットで Add Renderer Feature を選択します。「Render Object」を選択します。
各レンダーオブジェクトをオーバーライドする場合、以下のことができます。
- イベントに関連付け、レンダーループの特定のタイミングに注入する
- レンダーキュー(透明または不透明)とレイヤーマスクによるフィルタリング
- 「Depth」と「Stencil」の設定に影響を与える
- カメラ設定の変更(有効視野と位置オフセット)

HDRP では、カスタムパスを使用して同様の効果を得ることができます。カスタムパスボリュームを使用してカスタムパスを設定する方法は、HDRP ボリュームを使用する場合と似ています。
カスタムパスでは、以下のことができます。
- シーン内のマテリアルの外観を変更する
- Unity がゲームオブジェクトをレンダリングする順序を変更する
- カメラバッファをシェーダーに読み込む
HDRP でカスタムパスボリュームを使用すると、余分なカメラの使用とそれに関連する追加のオーバーヘッドを回避できます。カスタムパスは、シェーダーとの相互作用に柔軟性があります。Custom Pass クラスは C# で拡張することもできます。

オブジェクトが遠くに移動すると、Level of Detail(LOD; 詳細レベル)は、よりシンプルなマテリアルとシェーダーで低解像度のメッシュを使用するように調整または切り替えます。これにより、GPU のパフォーマンスが強化されます。
詳細については、Unity Learn の Working with LODs コースを参照してください。

ポストプロセッシングエフェクトをプロファイリングして、GPU のコストを確認します。ブルームや被写界深度など、フルスクリーンエフェクトにはコストがかかるものもありますが、ビジュアル品質とパフォーマンスの満足のいくバランスが見つかるまで試してみてください。
ポストプロセッシングはランタイム時にあまり変動しない傾向があります。Volume Overrides を決定したら、合計フレーム予算の静的な部分をポストエフェクトに割り当てます。

テッセレーションはシェイプをそのシェイプのより小さなバージョンに分割します。これにより、ジオメトリを増やしてディテールを強化できます。リアルな樹皮など、テッセレーションが適している場合もありますが、一般的にはコンソールでのテッセレーションは避けてください。GPU で負荷が高くなる可能性があります。
テッセレーションシェーダーと同様に、ジオメトリシェーダーと頂点シェーダーは GPU 上で 1 フレームにつき 2 回実行できます。1 回は深度プレパスの間、もう 1 回はシャドウパスの間です。
GPU で頂点データを生成または変更する場合は、多くの場合、ジオメトリシェーダーよりもコンピュートシェーダーが適しています。コンピュートシェーダーで作業を行うことで、ジオメトリを実際にレンダリングする頂点シェーダーを比較的高速かつシンプルにすることができます。
シェーダーのコアコンセプトの詳細をご覧ください。
GPU にドローコールを送信すると、その処理は多くのウェーブフロントに分割され、Unity は GPU 内の利用可能な SIMD 全体に分散します。
各 SIMD には、同時に実行できるウェーブフロントの最大数があります。ウェーブフロント占有率とは、現在使用されているウェーブフロントの最大数に対する比率のことです。GPU のポテンシャルをどれだけうまく活用しているかを測定します。コンソール固有のパフォーマンス分析ツールは、ウェーブフロントの占有率を詳細に表示します。
以下の例では、頂点シェーダーのウェーブフロントが緑色で表示されています。ピクセルシェーダーのウェーブフロントが青で表示されます。下のグラフでは、ピクセルシェーダーがほとんど動作していないにもかかわらず、頂点シェーダーのウェーブフロントが多数表示されています。これは、GPU のポテンシャルが十分に活用されていないことを示しています。
ピクセル化しない頂点シェーダーの作業を頻繁に行っている場合は、非効率である可能性があります。ウェーブフロントの占有率が低いことは必ずしも悪いことではありませんが、シェーダーの最適化や他のボトルネックのチェックを開始するための指標です。例えば、メモリや演算処理が原因で停止した場合、占有率を上げるとパフォーマンスが向上することがあります。一方、処理中のウェーブフロントが多すぎると、キャッシュスラッシングが発生し、パフォーマンスが低下する可能性があります。

GPU を十分に活用していないインターバルがある場合、Async Compute を使用すると、有用なコンピュートシェーダーの作業をグラフィックスキューに並行して移動できます。これにより、これらの GPU リソースをより有効に活用できます。
例えば、シャドウマップの生成中、GPU は深度のみのレンダリングを実行します。この時点ではピクセルシェーダーの処理はほとんど行われず、多くのウェーブフロントが占有されないままになっています。
コンピュートシェーダーの作業を深度のみのレンダリングと同期させることができれば、GPU の全体的な使用率が向上します。未使用のウェーブフロントは、スクリーンスペースアンビエントオクルージョンや、現在の作業を補完するタスクに役立ちます。
Unite のハイエンドコンソールのパフォーマンス最適化に関するセッションをご覧ください。

GPU Resident Drawer(URPとHDRPの両方で利用可能)は、CPU時間を最適化するように設計されたGPU駆動のレンダリングシステムで、パフォーマンスに大きなメリットをもたらします。クロスプラットフォームレンダリングをサポートし、既存のプロジェクトですぐに使用できるように設計されています。
このシステムは、HDRP または URP レンダーパイプラインアセット内で有効にできます。「Instanced Drawing」を選択して有効にします。また、再生モードのみで有効にするか、編集モードでも有効にするかを選択できます。
GPU Resident Drawer を有効にすると、ドローコールの回数が多いため CPU 依存のゲームでも、ドローコールの回数が減ることでパフォーマンスが向上します。改善は、シーンの規模と利用するインスタンシングの量によって異なります。レンダリングするインスタンス可能なオブジェクトが多いほど、メリットは大きくなります。
Instanced Drawing オプションを選択すると、UI に「BatchRenderGroup Variants 設定は『Keep All』にする必要がある」という警告メッセージが表示されることがあります。グラフィックス設定でこのオプションを調整すると、GPU Resident Drawer の設定が完了します。
詳細については、こちらのディスカッションスレッドをご覧ください。

URPとHDRPの両方で利用可能なGPUオクルージョンカリングは、GPU Resident Drawerと連携して動作します。各フレームのオーバードローの量を減らすことでパフォーマンスを大幅に向上させ、レンダラーが目に見えないものを描画してリソースを浪費しないようにします。
GPU オクルージョンカリングを有効にするには、レンダーパイプラインアセットを探し、「GPU Occlusion」チェックボックスをオンにします。
Unity 6 のデバッグモードで GPU オクルージョンカリングを有効にするには、「Window」>「Rendering」>「Occlusion Culling」を選択します。ここでは、さまざまなビジュアライゼーションオプションを切り替えることで、オブジェクトがどのようにカリングされているかを視覚化できます。


Unity のベストプラクティスハブでは、上級 Unity 開発者やクリエイター向けのベストプラクティスやヒントをさらに多数見つけることができます。Unity のツールセットやシステムを使って効率的に開発するのに役立つ、業界のエキスパートや Unity のエンジニア、テクニカルアーティストが作成した 30 以上のガイドからお選びください。