Unity を検索

カメラの使い方を工夫してゲームのパフォーマンスを最適化する方法:パート 2

2021年10月14日 カテゴリ: ゲーム | 7 分 で読めます
Accelerate Success Logo
Accelerate Success Logo
取り上げているトピック
シェア

カメラの使い方を工夫してゲームのパフォーマンスを最適化する方法のパート 2 へようこそ。パート 1 をまだお読みでない方は、こちらのリンクからぜひご一読ください 。では、前回の続きでプロファイラーの結果を見てみましょう。

ビルトインレンダーパイプラインのパフォーマンス

ビルトインレンダーパイプラインでは、Camera.Render プロファイラーマーカーは、メインスレッドで各カメラの処理にかかった時間を計測します。

 Profiler timeline view
2 つのカメラによるメインスレッドのプロファイラータイムラインビュー

各カメラには Camera.Render マーカーがあり、以下のグラフでは各テストケースごとに合算したものを表示しています。

Time spent processing Cameras on the main thread
メインスレッドでカメラの処理にかかる時間(小さい方が良い)

高負荷シナリオでは、デバイスごとに異なる負荷率を選択することで、長くなりすぎるということを避けつつ、合計のフレームタイムを長くすることができました。負荷が非常に高い場合は、カメラの追加コストがフレーム間のパフォーマンスのばらつきによって隠されてしまうので、信頼できる結果は得られません。

Time spent processing Cameras on the main thread
メインスレッドでカメラの処理にかかる時間(小さい方が良い)

傾向は明らかで、Camera.Render が費やす時間はカメラの数に直接影響されます。4 つ目のシーンで何もレンダリングしないカメラを追加しても、この結果は変わりません。

ユニバーサルレンダーパイプラインでのパフォーマンス

ユニバーサルレンダーパイプライン(URP)に移行すると、プロファイラーのタイムラインビューで最初に目につくのは、多くのバーが緑(レンダリング)ではなく青(スクリプト)になっていることです。緑色のバーは、Unity エンジンのレンダリングコードの C++ 側で費やされた時間を表しています。C++ 側には、ビルトインレンダリングパイプラインに含まれるすべてのレンダリングコードがあります。URP はスクリプタブルレンダーパイプラインの 1 つで、レンダリングコードの多くが C# 側に移され、ユーザーがレンダリングプロセスをカスタマイズできる範囲が大きく広がりました。

Inl_UniversalRenderPipeline.RenderSingleCamera プロファイラーマーカーは、メインスレッドで各カメラの処理にかかった時間を計測します。便利なことに、これらのマーカーには、それぞれのカメラの名前が接尾語として含まれています。

Profiler timeline view of the main thread with two Cameras
2 つのカメラによるメインスレッドのプロファイラータイムラインビュー

しかし、ビルトインレンダーパイプラインのテストのように、それらのカメラのマーカーの数値を合計しても、URP の総合的なパフォーマンスを正確に把握することはできません。上の図に見られるように、Inl_Context.Submit マーカーに費やされた時間がかなりあり、これもカウントする必要があります。これは、ビルトインレンダーパイプラインの Camera.Render マーカーに含まれていた描画コマンドバッファーの作成にかかる時間です。より簡単にするために、これらすべてを網羅している RenderPipelineManager.DoRenderLoop_Internal マーカーを選択します。

Time spent processing Cameras on the main thread
メインスレッドでカメラの処理にかかる時間(小さい方が良い)

一貫性を保つために、ビルトインレンダーパイプラインのシナリオと同じ高い負荷率を使用しました。

Time spent processing Cameras on the main thread
メインスレッドでカメラの処理にかかる時間(小さい方が良い)

ここでも傾向は明らかで、レンダリングコードが費やす時間はカメラの数に直接影響されます。ビルトインレンダーパイプラインのテストのように、4 つ目のシーンで何もレンダリングしないカメラを追加しても、この結果は変わりません。

この時点で、ビルトインレンダーパイプラインと URP のパフォーマンス特性をよく比較してみると、奇妙な結果が出ていることに気づくかもしれません。その気づきはおそらく正しいです。例えば、高負荷のテストでは、Galaxy S7 Edge では URP がビルトインレンダーパイプラインよりもはるかに効率的ですが、今回テストした iPhone モデルではそうではありませんでした。この記事を適度な長さにまとめ、主要なテーマに焦点を当て続けるために、これについては別のブログ記事で調査します。

避けたいカメラの使用パターン

ここでは、私たちが実際に目にした複数のカメラを使用するシナリオを検証し、その代替案を検討します。

シーンビューで巨大なキャンバスがシーンの真ん中にあると、気が散ってしまいます。一部のユーザーはこの問題を解決するために、別の UI カメラを離れた場所に置き、レンダーモードを「Screen Space - Camera」に設定したキャンバスをレンダリングしています。Unity 2019 からは、代わりにヒエラルキーウィンドウで子のゲームオブジェクトの表示・非表示を切り替えられます。これで、気を散らすキャンバスを隠すことができます。Unity のツールバーにある Layers ドロップダウンメニューもこの目的で使うことができます。

Hiding UI canvases using the Layers drop-down menu
Layers ドロップダウンメニューで UI キャンバスを隠す

ユーザーの中には、カメラを駆使してキャンバスの表示順を調整する人もいます。しかし、カメラはそういう使い方をするツールではありません。代わりに、キャンバスの「Sort Order」または「Plane Distance」 を使いましょう。ただし、入れ子になっているキャンバスには「Override Sorting」というオプションがあります。これは考慮に入れておくべきでしょう。

また、UI 画面の可視性を切り替えるために、カリングマスクを使用してゲーム UI の異なる部分をレンダリングする別々のカメラを使用するケースも見られます。これを実現するには、ゲームオブジェクトの有効・無効や Canvas コンポーネントの enable フラグを切り替えるのが正しい方法です。

最後に、UI に関係のない例として、複数のカメラを使って視点を切り替えるケースを紹介します。最悪なのは、これらのカメラがすべて有効になっていて、カメラのレンダリング順序(すなわち、Depth プロパティ)を使用して、どのカメラが表示されるかを制御している場合です。その場合、すべてのカメラが 1 台ずつレンダリングされることになり、非常にコストがかかります。使っていないカメラを無効にすると、このコストがなくなります。いろいろな例を見てきましたが、結局私たちは、カメラは 1 つだけ使い、常にその時有効にする視点にカメラを配置するのがベストだと考えています。これにより、誤って複数のカメラを有効にすることがなくなり、またカメラの管理プロセスも簡素化されます。

複数のカメラを使うべき場合

不必要に複数のカメラを使用することは避けるべきですが、それが最善、あるいは唯一の解決策となる場合もあります。一般的に、以下に挙げる要素を複数実現する必要がある場合は、複数のカメラを使うのは正しい選択です。

  • カメラ出力。これには、レンダリングサーフェス(Display、RenderTexture)とビューポートの矩形が含まれます。
  • 解像度。1 つのカメラの出力は、1 つの解像度にしか設定できません。しかし、カメラが使用するレンダリングパイプライン内の中間的な結果は、任意の解像度でレンダリングされ、カメラの出力に使用されます。その一例が HDRP の Low-Resolution Transparent パスです。
  • 視野角、位置、向き。これらのパラメーターは、カリングに使う視錐台を直接定義します。例外として、XR では、Unity がいくつかのトリックを使って、カメラが非常に近い位置にある 2 つの目を表現しています。

ここでは、複数のカメラを適切に使用する場合の例をご紹介します。

ディスプレイの解像度が非常に高い新しい(モバイル)デバイスで GPU のパフォーマンスを向上させるための一般的な方法は、シーンを低解像度でレンダリングし、それを最終的な解像度にアップスケールすることです。このシナリオでは、多くのゲームが、UI のスプライトや画像をよりシャープにするために、UI の少なくとも一部をアップスケールされたシーンの上に、ネイティブの解像度でレンダリングしたいと考えます。このタイプのレンダリング構成では、2 つの異なる解像度を使用するため、別にカメラが必要となります。

また、カメラの位置や解像度が異なる複数のサブディスプレイには、複数のカメラが必要です。こうした場合の例としてそれぞれのプレイヤーが独立して視点を動かせるようになっている分割画面のゲームがあります。

シーンの一部を第 2 の視点から見せる動的なビルボードには、それ専用に使うテクスチャとして RenderTexture が必要です。この RenderTexture のコンテンツを生成するには、別にカメラが必要です。

結論

このブログ記事シリーズでは、Unity のビルトインレンダーパイプラインとユニバーサルレンダーパイプラインでカメラを追加した場合のコストを測定しました。その結果、シーン内に不要なカメラがあるとコストがかかりますが、それを回避することで簡単にパフォーマンスを向上させることができることが明らかになりました。

最後に、何もレンダリングしないカメラでも、なぜこれほどまでにパフォーマンスに影響を与えるのか、不思議に思われるかもしれません。第 1 の理由は、カメラに何も入っていないことを確認するために、Unity に多くの作業が要求されることです。第 2 の理由は、率直に言って、Unity は最適ではないカメラの設定に最適化されていないことです。最適ではないカメラの設定に合わせてエンジンを最適化すると、適切に設定されたゲームほど遅くなり、メモリ使用量も多くなることとなり、望ましくありません。

Accelerate Solutions Games チームについて、およびこのチームがどのように皆さんのゲームを改善するか知りたいですか。Unity のホームページをご覧になるか、Unity の営業担当者にご連絡いただき、私たちがどのようにして皆さんの次のプロジェクトを加速させる手助けができるか、ご確認ください。 

私たちの Accelerate Success コンテンツシリーズを気に入ってくださった方には、最新の Accelerate Success ウェビナーの録画もチェックされることをおすすめします。Unity の U Iの刷新について、チームリーダーの Andrea と Sebastian が説明を行っています。このデモでは、Andrea と Sebastian が、あまり適切に設計されていない UI を取り上げ、ゲームがより速く、より効率的に動作するように UI を改善するためのヒントやベストプラクティスを紹介します。 

2021年10月14日 カテゴリ: ゲーム | 7 分 で読めます
取り上げているトピック
関連する投稿