パフォーマンスが良いことは、多くのゲームにとって重要なことです。ここでは、ゲームのレンダリングのスピードを最高にするための簡単なガイドラインを紹介します。
ゲームのグラフィカルな部分は、コンピューターの 2 つのシステム、GPU と CPU に主に影響を与えます。どんな最適化であっても最も重要なことは パフォーマンスの問題がどこにあるか を見つけることです。なぜなら、GPU と CPU では最適化の内容がかなり異なるからです (相反する内容になる場合もあります。例えば、良くあることとして CPU の最適化による GPU の処理の増加、またはその逆など)。
よくあるボトルネックとそれをチェックする方法は以下のとおりです。
あまり一般的ではないボトルネックは以下の通りです。
画面のオブジェクトをレンダリングするために CPU は多くの処理を行っています。どのライトがオブジェクトに影響するか判断する、シェーダーとシェーダーパラメーターの設定、グラフィックスドライバーに描画コマンドを送信する、その後、グラフィックドライバーはグラフィックスカードへ送信するコマンドを準備する、などです。
これらすべての “オブジェクトごとの” CPU 使用はリソースがかかるため、可視のオブジェクトが多いと CPU の処理が堆積する可能性があります。例えば 1000 個の三角形がある場合、CPU にとっては、それぞれの三角形で 1 つのメッシュ (加算すると 1000 メッシュになります) を処理するよりも、すべての三角形が 1 つのメッシュにまとまっているほうがよほど簡単です。この 2 つのシナリオの GPU の負担はほとんど変わりませんが、CPU が (1 つのオブジェクトではなく) 1000 のオブジェクトをレンダリングする負荷は、著しく高くなります。
可視のオブジェクトの数を減らします。CPU の処理量を削減する方法は以下のとおりです。
オブジェクトをまとめて、各メッシュに少なくとも数百の三角形が含まれるようにして、メッシュ全体で マテリアル を 1 つだけ使用するようにします。重要なことですが、マテリアルが共通でない 2 つのオブジェクトをまとめてもパフォーマンスはまったく向上しません。複数のマテリアルが必要となる最も一般的な理由は 2 つのメッシュが同じテクスチャを使用していないということです。CPU パフォーマンスを最適化するには、共通のテクスチャをもつオブジェクトをまとめるよう気を付けてください。
Forward Rendering パス でピクセルライトを多く使用する場合、後述するようにオブジェクトをまとめることが無意味な場合があります。これについては、後述のライティングパフォーマンスを参照してください。
OnDemandRendering を使用してアプリケーションのレンダリング速度を制御することで、CPU のパフォーマンスを向上させます。
以下のような場合には、フレームレートを下げたほうがよいでしょう。
レンダリング速度を調整することで、電力使用量やデバイスの熱を管理し、バッテリー駆動時間の最大化や CPU のスロットリングを防ぐことができます。特に、Adaptive Performance パッケージ と共に使うと特に効果的です。フレームが頻繁にレンダリングされなくても、アプリケーションは通常のペースでスクリプトにイベントを送信します (例えば、レンダリングされていないフレームの間に入力を受け取る場合があります)。入力の遅れを防ぐには、OnDemandRendering.renderFrameInterval = 1
を入力の間に呼び出すことで、動きやボタンなどが反応しているように表示することができます。
スクリプト、物理、アニメーションなどの処理が非常に多く、レンダリングが少ない状況では、この API を使用するメリットはありません。アプリケーションのビジュアルは、電力使用量への影響を最小限に抑えながらも、もたつく可能性があります。
ノート: VR アプリケーションは OnDemandRendering をサポートしていません。すべてのフレームをレンダリングしないと、頭の動きに合わせて映像が同期しなくなり、動きによる酔いのリスクが高まる可能性があります。
モデルのジオメトリを最適化するには 2 つの基本的ルールがあります。
通常、グラフィックハードウェアが処理する実際の頂点数は 3D アプリケーションで表示される頂点数とは一致しないことに注意してください。モデリングアプリケーションは通常、ジオメトリ頂点数、すなわちモデルを構成する角の数、を表示します。しかし、グラフィックスカードにとっては、レンダリングの目的で 2 つ以上の論理的な頂点に分ける必要があるジオメトリ頂点があります。頂点は複数の法線、UV 座標、または頂点カラーがある場合に分割する必要があります。結果的に、Unity でカウントされる頂点数は 3D アプリケーションで表示される数よりも、常に大きくなります。
モデルのジオメトリの数は、たいてい GPU に関連がある一方で、Unity のいくつかの機能は例えばメッシュスキンのように CPU でモデルを処理します。
Unity 以外の 3D アプリケーションでアセットを作成する際のパフォーマンスを向上させるためのヒントは、最適なパフォーマンスのためのモデル作成 を参照してください。
最速の方法は、常に、計算が不要なライティングを行うことです。そのためには、フレーム毎にライティングを計算するのをやめて、ライトマップ を使用して静的なライトを 1 回だけ “ベイク” します。ライトマップを適用して環境を製作する処理は、単に Unity のシーンにライトを配置するよりも多少時間がかかります。 その一方で 以下の利点があります。
多くの場合、複数の余分なライトを追加する代わりに、簡単なコツを適用できます。例えば、 Rim Lighting 効果のためにカメラに向かって直接を光を差し込むライトの代わりに専用の Rim Lighting の計算をシェーダーに直接追加します(詳しくは サーフェスシェーダーの例 を参照してください)。
フォワードレンダリングパスの詳細 も参照してください。
動的なピクセル単位のライティングでは、影響する全てのピクセルにレンダリング作業が追加されるので、オブジェクトをマルチパスでレンダリングする事になります。モバイルや ローエンド PC の GPU など処理能力の低いデバイスでは、オブジェクトを複数の ピクセルライト で照らすことは避け、毎フレームライティングを計算する代わりに、ライトマップを使用して静的なオブジェクトをライティングしてください。動的な頂点単位のライティングも、頂点をトランスフォームすると顕著にコストが増加します。複数のライトでオブジェクトを照らすことは、なるべく避けるようにしてください。
様々なピクセルライトで照らすために、十分距離を置いているメッシュをまとめることは避けてください。ピクセルライトを使用するとき、各メッシュはそれを照らすピクセルライトの数だけレンダリングされなければなりません。とても距離のある 2 つのメッシュをまとめると、まとめたオブジェクトの有効サイズが増加します。レンダリングのときには、ひとまとめにしたオブジェクトのあらゆる部分を照らしているピクセルライトすべてが考慮されます。そのため、必要なレンダリングパスの数は増加します。一般的に、まとめられたオブジェクトをレンダリングするために必要なパスの数は、離れたオブジェクトそれぞれのパスの合計数で、メッシュをまとめても何の意味もありません。
レンダリング中、Unity はメッシュの周囲のすべてのライトを見つけ、メッシュに最も影響するライトを計算します。Quality ウィンドウの設定が、ピクセルライトとして使用するライトの数と、頂点ライトとして使用するライトの数の変更に使用されます。各ライトは、メッシュからの距離、ライトの強さに基づいて重要度が計算されます。ライトの中には、純粋にゲームのコンテキストのために他よりもずっと重要なものがあります。このため、すべてのライトには Render Mode 設定があり、Important または Not Important に設定することができます。 Not Important に設定されたライトのレンダリングオーバーヘッドは低くなります。
例としてカーレースのゲームで、プレイヤーの車が暗闇の中でヘッドライトをつけて走っているとします。ヘッドライトはもっとも重要な光源となるため、Render Mode はおそらく Important に設定されます。逆に、ゲームの中で重要性がやや劣るライト (他の車のバックライト、街灯柱、等) およびピクセルライトにしてもビジュアル効果が改善されないライトがあります。それらのライトの Render Mode を Not Important に無難に設定することで、効果が薄いところに無駄なレンダリングコストを消費することを避けます。
ピクセル単位のライティングの最適化は CPU と GPU の両方を節約します。CPU が処理するドローコールが減り、GPU が処理する頂点数と、余分なオブジェクトレンダリングでラスタライズするピクセル数が減ります。
テクスチャのサイズを小さくするには圧縮テクスチャを使用します。 これにより、読み込み時間を短縮し、メモリフットプリントを縮小し、レンダリングパフォーマンスを大幅に向上できます。圧縮テクスチャは、圧縮されていない 32 ビット RGBA テクスチャに必要なメモリ帯域幅のわずかな部分しか使用しません。
3D シーンで使用されるテクスチャでは、常に Generate Mip Maps を有効にしてください。ミップマップテクスチャは GPU を有効にし、小さな三角形では低解像度を使用します。圧縮テクスチャにより GPU がレンダリングの際に転送されるテクスチャデータの量が制限されるのと同様です。
このルールで唯一の例外となるのはテクセル(テクスチャピクセル)がレンダリングされた画面ピクセルに 1 : 1 でマッピングする、すなわち UI 要素または 2D ゲームのときです。
カリングオブジェクトは、オブジェクトを非表示にすることに関与しています。CPU と GPU のロードを削減する効果的な方法の 1つです。
多くのゲームでは、プレイヤー体験の質を損ねずにこれを行う手っ取り早くて効果的な方法は、小さなオブジェクトを大きなオブジェクトと比べてより積極的に無視することです。例えば、小さな岩や砂塵は遠い距離では見えなくして、大きな建物は表示させたままにします。
様々な方法でこれを行うことができます。
Level Of Detail システムの利用
カメラのレイヤーごとのカリング距離を手動で設定
小さなオブジェクトは 別のレイヤー に置き、Camera.layerCullDistances スクリプト関数を使用してレイヤーごとのカリング距離を設定します。
リアルタイムシャドウは効果的ですが、パフォーマンスにかなり影響を与えるもので、CPU での余分なドローコールと、GPU で追加の処理を要します。詳細については、Light のトラブルシューティングとパフォーマンス を参照してください。
異なる種類のプラットフォームには、かなり大きなパフォーマンス能力の違いがあります。ハイエンドの PC GPU はグラフィックスとシェーダーの面で、ローエンドのモバイル GPU に比べ、ずいぶん多くを処理できます。個々のプラットフォームに関しても同じことが言えます。GPU が速いと、GPU が遅いものに比べ、十数倍速く処理を行います。
モバイルプラットフォームおよびローエンド PC での GPU パフォーマンス は、多分あなたの開発機より遥かに遅いでしょう。ローエンド GPU 機器でよいパフォーマンスを得るために、シェーダーを手動で最適化して計算量およびテクスチャ読み込みを削減することを推奨します。例えば、Unity のいくつかのビルトインシェーダーは「モバイル」と同等の仕様があり、より高速です。ただし、制限事項や近似があります。
以下は、モバイルやローエンド PC のグラフィックスカードのためのガイドラインです。
超越関数 (pow
、exp
、log
、cos
、sin
、tan
など) はとても負荷が高く、可能な場合は使用を避けてください。可能な場合は、複雑な数学的計算の代替案としてルックアップテクスチャの使用を検討してください。
独自の操作 (normalize
、dot
、inversesqrt
など) を書くことを避けてください。Unity のビルトインオプションは、ドライバがより優れたコードを生成できることを確証しています。アルファテスト (discard
) の操作がしばしばフラグメントシェーダーを遅くすることに気を付けてください。
浮動小数点数の変数の精度 (float
対 half
対 fixed
) が、
デスクトップ GPU ではほとんど無視される一方で、
モバイル GPU でよいパフォーマンスを得ることがかなり重要になっています。
詳しくは、シェーダーのデータタイプと精度
を参照してください。
シェーダーパフォーマンスの詳細については シェーダーを書く場合のパフォーマンスのヒント を参照してください。
Static
を設定して、ドローコールバッチング のような内部の最適化を許容します。ピクセルライト
(完璧にディレクショナルな) を複数でなく、 1つだけ設定するようにします。半
精度浮動小数点数の変数を使用してください。pow
、sin
、cos
などの複雑な数値計算の使用を最小限に抑えます。