見出し画像

【Unity】グラフィック軽量化101

 こんにちは、オオブチです。ゲームでコマ落ちしたりしてサクサク動かないとストレス溜まるな~~~と思う事って誰しもあることだと思うのですが、この現象はゲームの内部的に何かの処理がとんでもなく重くてボトルネックになっているのでフレーム数が出ない、という状態になっているのは想像に難くないと思います。PCで動画として見るなら2K30fpsは欲しいし、場合によっては4K60fps必須だったりVRだと90fps欲しいなんて話もありますが、それは例えば30fpsの絵を出すためには毎フレームの絵を出すための全部の処理が1/30秒=33ms程度で完了しなければならないという事でもあります。軽さは正義。
 動作を軽くするために行う諸々の作業を軽量化、最適化、パフォーマンスチューニングみたいな言い方をしますが今回はこの辺の話を少しだけしようという事で、グラフィック周りのパフォーマンスの確認方法について少し書きたいと思います。


1. Stats

 まずは幾つか覚えておきたい数字を並べてみます。Unityでは絵を出すときに、ざっくりマテリアルごと、オブジェクトごとに描画命令をグラフィックスAPIに出していて、この一回分の処理単位をバッチと呼び、いくつかのバッチをまとめてしまう事をバッチング(Batching)と言います。まとめてしまうというのは言い換えれば、同じマテリアルの設定なら一緒のメッシュとして同じ描画の命令で一気に処理してしまえ!という事です。
 またSetPass Callsというのはこのバッチごとに描画設定をCPUからGPUへ教える回数の事です。シェーダを書いたりしたことがある人はPassという単語に見覚えある人もいるかもしれません。SetPass Callで受けた指示を元にシーン中のオブジェクトが描画され、全部のオブジェクトが描画されることでやっとGameViewの絵が出来上がります。
 具体的にバッチングとかってどこで確認するのかというと、ゲームビュー右上のStatsをクリックするとわかります。

例1

 ここのシーンではバッチ(Batches)が7個でSetPass Callsも7、描画の処理単位が7個で全部個別の描画設定、バッチングはないっぽいという事になります(Saved by batchingが0)。

例2

 こちらの例だと3743個のバッチがバッチングされて、バッチが2439個あることになります。CPUから毎フレーム数百-数千回も命令を出すと前の例よりはやはりフレーム数は下がります。メインスレッドが35.2ms、レンダースレッドが20.1msの時間を使っていて、これで30fps(33.3ms以内)を目指すならもう少しだけ軽くしたいなあという事になります(まあこのぐらいならビルドすれば問題にならなかったりしますが)。

 BatchingとかSetPass Callsの数字はモデルの頂点数とかによっても変わることはあるらしく数えること自体にはあまり意味がないので、「少ないほうがサクサクな傾向」ぐらいの認識でいいんじゃなかと個人的には思います。

2. Profiler

 もう少し具体的に何の処理がどのぐらい負担になっているのか調べようという時に見るのがプロファイラーです。Window->Analysis->Profilerから開きます。ここでは再生開始から時系列で何の処理にどのくらいリソースを割いたかまとめて見ることができます。

Profilerの計測データ

 このWindowではStatsよりももっと具体的な処理内容とか数字をみて重くなっている原因を探します。見ることができる項目はCPU Usage、Rendering、Memory、Audioとか山ほどありますが、例えばCPU Usageの列をクリックすると処理内容とその概要を下の方のエリアで見ることができます。EditorLoop(グラフのカーキ色)はUnityエディタ自身が使っているリソースなのでここは飛ばしてPlayerLoopの方を展開していくと、この画面の場合だとどうやらReflectionProbeが重そうだという事がわかりました。

ReflectionProbeの更新だけで15ms近くも使っている

 確認すると実際にこのシーンではReflectionProbe のCubemapを最大解像度でリアルタイムに更新する設定になっていました。この場合は、解像度は落とせないのか、リアルタイムである必要はあるか、反射の質感を落としたくないなら他に共通にできるマテリアルはないか、ライティングもう少し軽くできないか、とかをこのタイミングで考えることになります。

3. Frame Debugger

 レンダリングに関してより具体的に見ていくときに使うのがFrameDebuggerです。Window->Analysis->FrameDebuggerから開きます。
 ここでは実際に個別のフレームを描画する際の描画処理の順番やマテリアルに使われているプロパティの値などを確認できます。大抵の描画周りのエラーとか負荷はこのあたりの情報で考えることが出来ます。

個別の描画処理について具体的な設定を確認できる

4. ちょっとだけ軽量化を試してみる

 長々と説明を書きましたが、何はともあれ少しだけ手を動かして実際に軽量化をやってみます。今回はこちらのアセットのサンプルシーンを参考にします。

今回はDemoSceneのFestival_Nightというサンプルを使用します。

まずは何も考えずにカメラだけ置いて実行してみます。

 日本の夏祭りを作れるとても良いアセットで、約1900ものプレハブを含む組み合わせが自由にできて大変使い勝手が良いのですが、このままのシーンではリアルタイムに描画させるのは厳しそうです。
まずはバッチングだけ有効にするためにシーン中のオブジェクトを雑に全部Staticにしてみます。

今回のシーンには効果がないっぽい

 無駄だったみたいです。SetPass Callsが11万オーバーともなればそれはそうでしょう。この例だと同じマテリアルをまとめて描画とか以前にマテリアルを一つのシーンに何万個も使っている訳がないので他を当たるべきでした。
 Shadow Castersの数字が多いのでお分かりの方もいらっしゃると思いますが実はこのシーンにはRealtime設定のライトがおびただしい量置いてあります。この辺りを解消すると使えるようになると信じて手を加えてみます。

提灯とか電球一個一個にLightが付いてる、、、

 Realtime設定のLightは重いことで有名です。ライトを当てるという事は当たったライト分の明るさの描画と影落ちの描画がライト毎に行われるので一個のオブジェクトに対して複数Passで描画されるからです。色んな照明を焚くと色んな方向に影も落ちますしライト数が多いとPass数も増える事は想像しやすいですね。
 軽量化をする時はまずライトをベイクしよう、と言い切ってもいいぐらいライトのベイクは効果を発揮します。ライトのベイクは読んで字のごとくライトにまつわる計算をテクスチャに焼いてしまう事です。動かない背景に落ちる光と影は同様に動かないのだから事前に1回計算してしまってずっと使い回し続ければいいでしょという事です。これによって光源と影落ちの分のSetPass Callsが削られて軽くなるはずです。
 ライトをベイクするときはベイクするライトがRealtimeではなくBakedになっている事、描画に使われるモデルのImport SettingsでGenerate Lightmap UVsにチェックが入っている事だけ忘れずに確認しましょう。

LightExprolerでModeとか影落ちとか強度とかまとめて確認できる
Import SettingsとかMeshRendererのコンポーネントでLightMap UV展開時の設定がちょっと触れる
LightMapUVでピクセルがかぶってる時とかの微調はこの辺りで
Bake時の設定例

ベイク後の状態でもう一度再生してベイク前後の比較してみます。

Before
After

 完全に一致とまではいきませんが見た目を程々に保ったままPCで動かすには充分なだけシーンを軽くすることができました。サンプルシーンはリアルタイム向きじゃなかっただけのようで安心しました。デモシーンとにかくたくさんのプレハブを置きたくなるのでつい重くなってしまいますよね。render threadが使っている時間が257.6msから7.9msまで下がって約1/33まで減っています。
 今回のシーンの様にあらかじめ置いていたライト数が多いので効果が分かりやすいというのはあるのですが、実際にはこれでもまだ不十分という場合もあり、そういう場合は例えば
・隠れて見えないものは非表示にする(Occlusion Culling)
・メッシュは結合するか
・別DCCツールなどを使って複数テクスチャを1枚にまとめることで複数マテリアルを共通にしてしまう(アトラス化)
・シェーダを選び直す/書き直す(SetPassCalls数が少なくても個別のPassが超重い場合)
・Meshの目の細かさを考えなおす or 距離で切り替える(LOD)
みたいな対応を考えます。これ以上書くと長くなるのでこの辺の話は別の機会に残します。

 またここまではグラフィック周りの軽量化についてのみ書いているので、当然スクリプトのUpdateの中を軽くするとか物理の更新頻度を変えてみるとか、グラフィック周りの他にも軽くできる要素は沢山あります。Profilerを見るとこの辺りも原因を探すことができるので「何だか作っているプロジェクトが重いな~~」と思ったらProfilerで何が起こっているか確認してみてください。

 以上今回はグラフィック周りの軽量化入門編という事でパフォーマンスのチェック方法の確認からライトのベイクまでをやってみました。さようなら。

この記事が気に入ったらサポートをしてみませんか?