3Dフォーマット
ドローコール バッチング

グラフィックス パフォーマンスの最適化

多くのゲームにとって良いパフォーマンスは決定的なことです。ゲームのグラフィックスのレンダリングのスピードを最大化するための簡単なガイドラインを以下でまとめます。

どこにグラフィックスのコストがかかるか

ゲームのグラフィックな部分は主にコンピュータの2つのシステムにコストがかかります: GPU または CPUです。どんあ最適化であっても最初にやるべきことは パフォーマンスの問題がどこにあるか を見つけることです。GPU と CPUでは最適化の内容はかなり異なります。(場合によっては相反する内容になります - 良くあることとして CPU 最適化で GPU の処理を増やしたり,その逆など)

良くあるボトルネックおよびそれをチェックする方法として:

  • GPU はしばしば フィルレート または メモリ帯域幅で制限されています
    • 低解像度でゲーム実行することにより高速化されますか?もしその場合は GPU のフィルレートにより制限されている可能性が高いです。
  • CPU はしばしばレンダリングする必要があるものの数,すなわち “ドロー コール”,に制限されます。
    • Rendering Statistics ウィンドウで “Draw Calls” をチェックします。もしPCで数千,モバイルで数百である場合,オブジェクト カウントを最適化するのが良いかもしれません。

もちろん,これは原則論です。ボトルネックは他のどこかにあるかもしれません。頻度はやや減りますが,他のボトルネックとして:

  • GPU , CPU ともにレンダリングが問題でない場合。例えば,スクリプトや物理演算が実際の問題かもしれません。 Profiler を使って調べて下さい。
  • GPU が処理する頂点数が多すぎる場合。どれだけの頂点数であれば “問題ない” かは GPUおよび頂点シェーダの複雑さに依存します。典型的な指標はモバイルで “十万以下” ,PCで “数百万以下” です。
  • CPU CPU が頂点の処理を行うものに対して頂点数が多すぎる場合。スキン メッシュ,クロス シミュレーション,パーティクル,等かもしれません。

CPU 最適化 - ドロー コール カウント

画面のオブジェクトをレンダリングするために CPU がやるべきことがいくつかあります - どのライトがオブジェクトに影響するか判断する,シェーダおよびシェーダ パラメータのセットアップ,描画コメンドをグラフィックス ドライバに送信する,次にグラフィックス カードへ送信するコマンドを準備する,等です。これらは “オブジェクト単位” の CPU コストは安価ではないため,表示されるオブジェクトがたくさんある場合,積もり積もってしまう場合があります。

だから例えば,千個の三角形があれば,それらがひとつのメッシュにあるほうが千個のメッシュが個別に三角形があるより安価です。 両方のケースで GPU コストはほぼ同様ですが,CPU が千個のオブジェクトをレンダリングすることはひとつに比べて顕著に差がでます。

CPU の処理量を減らすため,表示されるオブジェクトカウントを減らすのは良い考えです:

  • 近いオブジェクトを手動または Unity の ドロー コール バッチング を使用してまとめます。
  • オブジェクトのマテリアル数を減らすために,別のテクスチャをより大きな テクスチャAtlasにまとめる等をします。
  • オブジェクトが複数回レンダリングされることを減らす(リフレクション,シャドウ,ピクセルライト,その他,以下で説明)

オブジェクトをまとめて,各メッシュが少なくとも数百の三角形をもつようにして,メッシュ全体で Material がひとつのみになるようにします。重要なことですが,マテリアルが共通でない二つのオブジェクトをまとめてもパフォーマンスは全く向上しません。複数のマテリアルを使用する一般的な理由は二つのメッシュが同じテクスチャを使用しないためなので, CPU パフォーマンスを最適化するには,オブジェクトをまとめる対象は共通のテクスチャをもつものに絞るべきです。

しかし, Forward Rendering パス でピクセルライトを多く使用する場合,後述するようにオブジェクトをまとめることが無意味な場合があります。

GPU: モデル ジオメトリの最適化

モデル ジオメトリの最適化を行うとき,基本的に二つのルールがあります:

  • 必要以上に三角形を使用しない
  • UV マッピングのシームおよびハードエッジ(二重化した頂点)をできるかぎり少ない数に抑える

実際にグラフィック ハードウェアが処理すべきは3Dアプリケーションで表示される数と通常は一致しないことに留意して下さい。モデリング アプリケーションは通常ジオメトリ頂点数,すなわちモデルを構成する角の数,を表示します。しかし,グラフィックス カードにとっては,いくつかのジオメトリ頂点は二つ以上の論理的な頂点に分ける必要があります。頂点は複数の法線,UV座標,または頂点カラーがある場合に分割する必要があります。結果的に,Unity の頂点数はつねには3Dアプリケーションで表示される数より大きくなります。

モデルのジオメトリの数が GPU にとってはもっとも関連がある一方で,Unity のいくつかの機能は例えばメッシュスキンのように CPU でモデルを処理します。

ライティング パフォーマンス

計算の必要がないライティングはつねに最速です。 Lightmapping で 毎フレームごと計算するのではなくライティングの “焼き込み” を一回のみ行って下さい。ライトマップ環境を生成するには,Unity上のシーンにライトを配置して,かかる時間はわずかです。 その一方で :

  • 遥かに早く実行される(2つのピクセルライトでは2–3倍)
  • さらにグローバル イルミネーションおよびライトマッパーの焼き込みにより結果をスムージングできるため外見も改善

多くの場合,あちこちにライトを配置する代わりに,シェーダおよびコンテンツでのテクニックがあります。例えば,“Rim Lighting” を得るためにカメラに直接を光を差し込むライトの代わりに専用の “Rim Lighting” 計算をシェーダに直接追加することを検討します。

Forward Rendering でのライト

動的な ピクセル ライティング により影響を受けるピクセルに顕著にオーバーヘッドが増加し,オブジェクトが複数のパスでレンダリングすることにつながる場合があります。モバイルやローエンドPCおGPUなど,それほど強力でないデバイスでは,複数の Pixel Light がいずれかのオブジェクトを照らすことは避け,ライトマップを使用して,毎フレームごとに計算させることなく,静的なオブジェクトをライティングして下さい。動的な頂点ライティングも頂点変換により顕著にコストを増加させます。複数のライトがいずれかのオブジェクトを照らすことを避けててください。

もしピクセル ライティングを使用する場合,ピクセライトにより各メッシュが照らされる回数だけレンダリングが行われます。もし二つの離れたメッシュをまとめた場合,まとめたオブジェクトの有効サイズを増やします。このまとめたオブジェクトの一部でも照らす全てのピクセルライトはレンダリングで考慮されるため,レンダリングパスの回数が増えるかもしれません。一般的にまとめたオブジェクトに対するレンダリングは各々の別のオブジェクトのパス回数の合計であり,まとめることにより得られるものはありません。この理由から異なるセットのピクセルライトにより影響されるぐらいに十分に離れたメッシュはまとめるべきではありません。

レンダリングの際に,Unityはメッシュを囲む全てのライトをみつけて,どのライトがそれに最も影響するかを計算します。Quality Settings によりいくつのライトがピクセルライトおよび頂点ライトとして残るかを調整することが出来ます。各ライトはメッシュからの距離および照らす強度にもとづいて各自の重要度を計算します。さらにいくつかのライトはゲームの流れにもとづいて重要度が異なります。この理由から,全てのライトは Render Mode 設定があり,Important または Not Important に設定できます。 Not Important に設定されたライトは一般的により低いレンダリング オーバーヘッドになります。

例としてカーレースのゲームで,プレイヤーの車が暗闇の中でヘッドライトをオンにして走っているとします。ヘッドライトはもっとも重要な光源となるため,Render Mode はおそらくImportant に設定されます。逆に,ゲームの中で重要性がやや劣るもの(他の車のバックライト,等)およびピクセルライトによりビジュアル効果が改善されない場合があります。それらのライトのRender Mode はNot Important に安全にセットすることで,効果が薄いところでは無駄なレンダリングの消費 を避けます。

ピクセル ライティング 最適化は CPU と GPU の両方を節約します: CPU が処理するドロー コールが減り, GPU が処理する頂点数,および,追加のオブジェクト レンダリングでラスタライズするピクセル数が減ります。

GPU: 圧縮テクスチャ と Mip Map

圧縮テクスチャ の使用によりテクスチャのサイズを削減(結果的にロードタイムの高速化およびメモリ使用の最小化)し,さらにレンダリング パフォーマンス を飛躍的に向上させる場合があります。圧縮テクスチャは 非圧縮 32ビットRGBAテクスチャと比べるとわずかなメモリ帯域幅しか使用しません。

テクスチャ Mip Map

原則として,3Dシーンで使用されるテクスチャでは Generate Mip Maps を有効化して下さい。圧縮テクスチャにより GPU がレンダリングの際に転送されるテクスチャデータの量を制限するのと同様に,Mip Mapされたテクスチャにより GPU が小さな三角形では低解像度を使用します。

このルールで唯一の例外となるのはテクセル(テクスチャ ピクセル)がレンダリングされた画面ピクセルに 1 : 1 でマッピングする,すなわち UI 要素または 2D ゲームのときです。

LOD およびレイヤーごとのカリング距離

いくつかのゲームでは積極的に小さなオブジェクトを大きなオブジェクトと比べて無視し, CPU および GPU ロードを削減することが適切です。例えば,小さな岩や砂塵は遠い距離では透明にして,大きな建物は表示させたままに出来ます。

これは Level Of Detail システムを使用するか,手動で レベル ごとの カリング距離をカメラに設定します。小さなオブジェクトは 別のレイヤー において,レベルごとのカリング距離を Camera.layerCullDistances スクリプト関数をしようしてセットアップできます。

リアルタイム シャドウ

リアルタイム シャドウ は良いけれども,パフォーマンスをかなり消費するものであり,CPU での余分なドロー コール に加えて, GPUで追加の処理を要します。詳細については,シャドウのページ を参照下さい。

GPU: パフォーマンス の高い シェーダ を書くヒント

ハイエンドPCの GPU および ローエンド モバイル GPU は事実上 100 倍の パフォーマンスの差があります。個々のプラットフォームの中でもバラツキは同様に大きいです。PC 上では,高速な GPU は 遅い統合された GPU より 10 数倍以上 高速です。さらにモバイルプラットフォームでは GPU でも同程度の差が GPU で見られます。

モバイルプラットフォーム でのGPU パフォーマンス および ローエンド PC はあなたの開発機より遥かに遅いものです。一般的には シェーダは手動で最適化して計算量およびテクスチャ読み込みを削減しないと良いパフォーマンスが得られません。例えば,Unityのいくつかのビルトイン シェーダは “モバイル” 同等物があり,より高速です(制限事項や近似はありますが,それらがまさに高速にさせる内容です)。

モバイルやローエンド PC のグラフィックス カードでもっとも重要なのを以下にガイドラインとしてまとめました:

複雑な数値計算

数学的な超越関数( pow, exp, log, cos, sin, tan, その他)はかなり高価であり,原則としてピクセルごとにひとつ以上そういう処理がないようにすべきです。可能なかぎりルックアップ テクスチャで可能なかぎり代替することを考えます。

一方で,カスタムで normalize, dot, inversesqrt の処理を記述して作成することは推奨できません。もし内蔵のものを使用すればドライバがより良いコードを生成してくれます。

アルファテスト(discard)によりフラグメントが遅くなることを頭に入れておいて下さい。

浮動小数点の処理

カスタムシェーダを記述するときは浮動小数点はつねに精度を指定すべきです。可能なかぎり最も小さい浮動小数点の形式を選択することはパフォーマンスにとって 決定的 なことです。処理の精度は多くのデスクトップ GPU では無視されますが,多くのモバイル GPU ではパフォーマンスにとって決定的です。

もし シェーダ が Cg/HLSL で記述されている場合,精度は次のように指定されます:

  • float - 32ビットの単精度浮動小数点の数値表現であり,頂点変換に適してますが,パフォーマンスは最も遅いです。
  • half - 16ビットの半精度浮動小数点数の数値表現であり,テクスチャ UV 座標に適していて,おおよそ highp の2 倍ぐらい速いです。
  • fixed - 10ビットの固定小数点数の数値表現であり,カラー,ライティング 計算,およびその他のハイパフォーマンスに適していて,おおよそ highp の4 倍ぐらい速いです。

もし シェーダ がGLSL ESで記述されている場合,浮動小数点の精度は各々 highp, mediump, lowp で指定されます。

シェーダ パフォーマンスの詳細については シェーダ パフォーマンス を参照下さい.

ゲームを高速化する際のチェックリスト

  • PC 向けでは,ターゲットとする GPUのスペックに合わせて 頂点数を20万から300万より少なくします。
  • ビルトイン シェーダを使用する場合, Mobile または Unlit のカテゴリーから選択する。モバイルでないプラットフォームでも動作しますが,より複雑なシェーダの簡素化され,近似化されたバージョンです。
  • シーンにおける異なるマテリアルの数を低く抑えます - 異なるオブジェクト間でマテリアルをできるかぎり共有します。
  • 動かないオブジェクトに対して, Static をセットして, Static Batching のような内部の最適化を許容します。
  • 不要なときは Pixel Lights を使用しません - ジオメトリに影響するのはひとつだけの(推奨は指向性ライト)ピクセルライトとします。
  • 不要なときは動的なライトは使用しません - その代わりにライティングの焼き込みを選択します。
  • 圧縮テクスチャ形式を可能な場合は使用する,その他の場合は32ビットよりも16ビットを選択します。
  • 不要なときはフォグを使用しません。
  • Occlusion Culling の長所を学んだうえで,複雑でstaticなシーンをオクルージョンにより,表示されるジオメトリの量およびドローコールを削減します。オクルージョン カリング の効果が出るようなレベルの設計をします。
  • Skybox を使用して 遠くのジオメトリを “本物に見せかけます” 。
  • ピクセル シェーダを使用するか,Texture Combiner を使用して,マルチパスではなく複数のテクスチャをミックスします。
  • もしカスタムシェーダを記述する場合は,つねに出来るかぎり小さい浮動小数点の数値表現を使用します:
    • fixed / lowp - カラー,ライティング情報および法線に適します
    • half / mediump - テクスチャ UV 座標に適します
    • float / highp - ピクセル シェーダでは避けて,頂点シェーダの位置計算に適します
  • 複雑な数値計算では使用を最小化し,例えば pow, sin, cos, 等をピクセルシェーダでの使用を最小化します
  • フラグメントごとのテクスチャをより少なくなるようにします

関連項目

3Dフォーマット
ドローコール バッチング