多くのゲームにとって高いパフォーマンスは重要なことです。ゲームのレンダリングのスピードを最大化するための簡単なガイドラインを以下でまとめます。
ゲームにおけるグラフィカルな部分は主にコンピューターの 2 つのシステム - GPU と CPU - に影響を与えます。どんな最適化であっても最も重要なことは パフォーマンスの問題がどこにあるか を見つけることです。なぜなら、GPU と CPU では最適化の内容がかなり異なるからです。(相反する内容になル場合もあります - 例えば、良くあることとして CPU の最適化による GPU の処理の増加、またはその逆など)
よくあるボトルネックとそれをチェックする方法は以下のとおり。
あまり一般的ではないボトルネック:
画面のオブジェクトをレンダリングするために CPU は多くの処理を行っています - どのライトがオブジェクトに影響するか判断する、シェーダーとシェーダーパラメーターの設定、グラフィックスドライバに描画コマンドを送信する (それで、グラフィックドライバはグラフィックスカードへ送信するコマンドを準備します) 、などです。
これらすべての「オブジェクトごとの」CPU 使用はリソースがかかるため、可視のオブジェクトが多いと作業が堆積する可能性があります。例えば 1000個の三角形がある場合、CPU にとっては、それぞれの三角形に 1つのメッシュ (加算すると 1000 メッシュになります) を処理するよりも、すべての三角形が 1つのメッシュにまとまっているほうがよほど簡単です。この 2つのシナリオで GPU の負担はほとんど変わりません。しかし、CPU が (1つのオブジェクトではなく) 1000 のオブジェクトをレンダリングする負荷は、著しく高くなります。
可視のオブジェクトの数を減らします。CPU の処理量を削減する方法は以下のとおりです。
オブジェクトをまとめて、各メッシュが少なくとも数百の三角形をもつようにして、メッシュ全体で__マテリアル__が 1つのみになるようにします。重要なことですが、マテリアルが共通でない 2つのオブジェクトをまとめてもパフォーマンスはまったく向上しません。複数のマテリアルが必要となる最も一般的な理由は 2つのメッシュが同じテクスチャを使用していないということです。CPU パフォーマンスを最適化するには、共通のテクスチャをもつオブジェクトをまとめるよう気を付けてください。
Forward Rendering パス でピクセルライトを多く使用する場合、後述するようにオブジェクトをまとめることが無意味な場合があります。これについては、後述のライティングパフォーマンスを参照してください。
モデルのジオメトリを最適化するには 2つの基本的ルールがあります。
通常、グラフィックハードウェアが処理する実際の頂点数は 3D アプリケーションで表示される頂点数とは一致しないことに注意してください。モデリングアプリケーションは通常、ジオメトリ頂点数、すなわちモデルを構成する角の数、を表示します。しかし、グラフィックスカードにとっては、いくつかのジオメトリ頂点はレンダリングの過程で二つ以上の論理的な頂点に分けられる必要があります。頂点は複数の法線、UV 座標、または頂点カラーがある場合に分割する必要があります。結果的に、Unity でカウントされる頂点数は 3D アプリケーションで表示される数よりも、常に大きくなります。
モデルのジオメトリの数は、たいてい GPU に関連がある一方で、Unity のいくつかの機能は例えばメッシュスキンのように CPU でモデルを処理します。
最速なのは、ライティングの計算をしない事です。そのためには、フレーム毎にライティングを計算するのをやめて、静的なライティングを一度に “ベイク” して ライトマップ を作成します。ライトマップを適用した環境を製作するのは、単に Unity 上でライトをシーンに置くよりも多少時間がかかります。 その一方で
多くの場合、複数の余分なライトを追加する代わりに、シェーダーおよびコンテンツでのテクニックがあります。例えば、 Rim Lighting 効果を得るためにカメラに直接を光を差し込むライトの代わりに専用の Rim Lighting の計算をシェーダーに直接追加します。(詳しくは サーフェイスシェーダーの例 を参照してください。)
Forward Rendering パスの詳細も参照してください。
動的なピクセル単位のライティングでは、影響する全てのピクセルにレンダリング作業が追加されるので、オブジェクトをマルチパスでレンダリングする事になります。モバイルや ローエンド PC の GPU など処理能力の低いデバイスでは、オブジェクトを複数の ピクセルライト で照らすことは避け、毎フレームライティングを計算する代わりに、ライトマップを使用して静的なオブジェクトをライティングしてください。動的な頂点単位のライティングも、頂点をトランスフォームすると顕著にコストが増加します。複数のライトでオブジェクトを照らすことは、なるべく避けるようにしてください。
異なるまとまりのピクセルライトで照らすために、十分距離を置いているメッシュをまとめることは避けてください。ピクセルライティングを使用するとき、各メッシュはそれを照らすピクセルライトの数だけレンダリングされなければなりません。とても距離のある 2つのメッシュをまとめると、まとめたオブジェクトの有効サイズが増加します。レンダリングのときには、ひとまとめにしたオブジェクトのあらゆる部分を照らしているピクセルライトすべてが考慮されます。そのため、必要なレンダリングパスの数は増加します。一般的に、まとめられたオブジェクトをレンダリングするために必要なパスの数は、離れたオブジェクトそれぞれのパスの合計数で、メッシュをまとめても何の意味もありません。
レンダリングの際に、Unity はメッシュを囲むすべてのライトをみつけて、どのライトがそれにもっとも影響するかを計算します。画質設定 を使用して、いくつのライトをピクセルライト、そしていくつを頂点ライトにするかを調整することができます。各ライトはメッシュからの距離および照らす強度にもとづいて、各自の重要度を計算します。いくつかのライトは純粋にゲームの流れに基づいて、他のライトよりもより重要です。このため、個々のライトには、 Important または Not Important を設定する Render Mode があります。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
などの複雑な数値計算の使用を最小限に抑えます。