見出し画像

【VRChat】ワールドの軽量化 ~描画の仕組みと計測方法を知って、効率よく軽量化しよう~

はじめに


今回のワールド製作者向け記事はワールドの軽量化について書きます。

とはいえ、軽量化と一口に言っても全部書こうとすると膨大な文章量になってしまい、分かりにくくなること間違いなしです。

そこで、今回はあえて軽量化技法の話は詳しくしないことにしました。

代表的な技法については『VRChatのワールド制作基礎能力ツリー』で一通り紹介していますし、他の記事でも時折ふれたりしているからです。

代わりに今回は
『重いものを見極める方法(測定方法)』と『何故重いのか(原理)』
の2つについてキチンと書くことで、初心者によくある
「何か良く分からないけど重く(軽く)なった」
を脱出するための手掛かりを提供しようと思います。

では長くなると思いますが、お付き合いください。

容量と描画負荷について

さて、この話は時折しているので聞き飽きた人も居るかもしれませんが、『ワールドの容量』はVRChatがカクカクになる『描画負荷』とは違いますよ、という話から始めたいと思います。

新鮮な話はないので、上の文章で分かる人はこの項を飛ばしていただいて結構です。

『ワールドの容量』というのは、VRChat内でどこかのワールドに移動するときにロードしている容量のことです。

ワールドによっては数百MBとかあって、回線速度が遅く「まだロード終わらないのかな」とじれったい思いをしている人も居らっしゃるでしょう。

ですが、この『ワールドの容量』が大きいからと言って、カクカクする重たいワールドかというと、そういうわけではありません。Vketとかは凄い容量ですが、必ずしも重たいワールドではありませんよね?

カクカクして重たい……というのはFPSと呼ばれるものが下がって描画が間に合ってない状態になります。このカクカクして重たい原因が『描画負荷』です。

FPSが下がることで何故カクカクするのかについては、ライトベイクの記事の冒頭にある『VRChatの映像が出来るまで』という項を読んでもらうと一通り分かります。

ここで理解してもらいたいのは、『ワールドの容量』と『描画負荷』が別物であり、それぞれ別に軽量化を行う必要があるということです。それぞれの軽量化に使う技法も異なりますし、計測の方法なども異なります。

これ以降の説明では、この二つを区別して説明していこうと思います。
まずは『ワールドの容量』についてです。

ワールドの容量とその計測方法について

『ワールドの容量』は先ほど説明した通り、ワールドに移動する時に読み込んでいるもの……ですが、これは何かというと画像や音源データ、3Dモデルのデータ等といったワールドを構成する材料になります。

材料は一度読み込んでしまえば、別のワールドに移動するまで(最近だとキャッシュ機能があるのでキャッシュが無くなるまで)保持されますので、読み込みはワールドに移動している間の1回きりです。

この材料をどうやって軽量化するかというと、基本的には圧縮と低解像度化です。

はっきり言ってしまうと基本的に品質を下げる形で軽量化するのですが、皆さんのPCのリソースは限られているので極力やるべきだと思います。

それにあまり嫌がる必要はありません。人間にとって“過剰な解像度”というのがあります。

俗っぽい話になりますが『芸能人格付けチェック』というバラエティー番組をご存じなら、それが良い例になると思います。超高級な何かと安物の何かが出されて、どちらが高級品か芸能人が当てる……というのが番組の趣旨ですが、結構間違う様子が見られます。

品質が途轍もなく良くても、人間にその”良さ”が判別できないことはあります。

転じて、ワールドに使う材料についても同じことが言えます。適切に圧縮と低解像度化を行えば、人間にはほとんど変わっていないように感じさせつつ、軽量化が出来ます。

では『ワールドの容量』を軽量化するにあたっての計測方法ですが、今回紹介するのは2つです。

まず1つ目は『VRWorld Toolkit』です。

簡単に説明するとワールドに使われている材料ごとの容量を一覧で確認することが出来ます。

他にもワールド制作に役立つ支援機能が含まれていて、それを含めた使い方については『ワールド制作時に必ず利用しているおすすめのツール群』という記事で紹介しているので、詳しく知りたければそちらを見てください。

そして重要情報ですが、現在は特に何もしなくてもVCCからボタン一つで導入できるようになり、とっても便利です。是非使いましょう。

もう一つはloxさんが作成した『AssetBundleStat』です。

こちらには前者にはない利点があります。

VRCSDKでワールドデータをアップロードする際、ビルドされたワールドデータには全体圧縮がかかります。ですが、VRWorld Toolkitの一覧で表示されている容量はこの圧縮がかかる前の数値になっています。

一方で、AssetBundleStatではこのビルド時の圧縮でどれくらい小さくなったかが可視化されています。ものによっては、ビルド時の圧縮の恩恵を受けやすいものと、そうでないものがあります。そこら辺を加味して軽量化したい場合は、こちらを使うといいでしょう。

『描画負荷』の軽量化、CPU負荷とGPU負荷について

お次は『描画負荷』の軽量化についてですが、こちらは少々ややこしいです。

何故かと言いますと、『描画負荷』は大まかに「CPUへの負荷」「GPUへの負荷」に分けられるからです。

CPUとGPUはパソコンの中のパーツの名前というのはご存知かもしれませんが、この二つは2人3脚でVRChatの映像を作ってくれています。

そのため、負荷が大きいほうを改善しない限り、一向に軽くなってくれないというケースもあります。

ですので、軽量化については闇雲に色んな技法を試してみるのではなく、まずは適切な計測を行って、どちらの負荷がネックになっているのかをはっきりさせることが重要です。その上で、その負荷軽減に効果的な技法を試すと良いでしょう。

『描画』されるまでのお話

さて、先ほどCPUとGPUが協力して、VRChatの映像を作っている……というお話をしたと思います。それについてもうちょっと踏み込んでお話していき、一体どこを軽量化しようとしているのかについて話していきたいと思います。

まず基本的な構造として、CPUがGPUに命令を出して、GPUが絵を描くという構造になっています。どうしてこのような役割分担になっているかというと、CPUとGPUの性質の違いがあります。

まずCPUは非常に賢く、色々な仕事ができます。ただし、仕事は基本的に一つずつしかすることが出来ません。
(補足:正確にはマルチコアやスレッドで少しは分担してたりする→参考

一方でGPUはあまり賢くなく、絵を描くという単純な作業しかできません。ですが複数の仕事を同時にこなすことが出来ます。

この関係性を例えるならCPUが総司令で、GPUが沢山いる作業員という感じでしょうか。

なんか微妙にディストピア感ありますね

CPUは賢いので絵もかくことが出来ますが、他にも沢山の仕事が回ってきますので、餅は餅屋ということでGPUに絵を描くように仕事のお願いをします。

GPUに描画をしてもらうには、二つの情報が必要になります。1つ目が描画するメッシュ(3Dモデルの形状データのこと)、2つ目がマテリアル(色をはじめとする見た目の情報のこと)です。

そこで、まずCPU側で一度に描画するメッシュ群をBatchと呼ばれる単位としてまとめます。このBatchごとにGPUにマテリアルの情報を伝える命令であるSetPass Callと描画命令であるDraw Callが呼ばれます。
(補足:SetPass Callは必要に応じて、呼ばれたり呼ばれなかったりします)

仮にSetPass Callが250回、Draw Callが750回呼ばれた場合、合計で1000回命令する必要があるわけですが、これは毎フレーム行われます。仮に90FPSだった場合たった1秒間に1000×90=90000回の命令が呼ばれてしまうわけです。

CPUは1つずつしか命令を出せませんので、あまりに命令の数が多いと絵を描くスピードが遅くなってきてしまいます。ですので、この命令の数を減らすことで軽量化を行うことが出来ます。

適当に作った図ですが

さて、描画命令が発行された後、GPUの出番です。

GPUでは与えられた命令を元に描画を行おうとします。
CPUから与えられる情報でメッシュや、利用するマテリアル情報などが分かっていますので後は描画するだけです。

そこでGPUは、マテリアルに設定されたシェーダーの内容に沿って絵を作っていきます。

シェーダーは絵をどのように作るかが規定されたプログラムです。そしてUnityではシェーダーのプログラムを自分で書くことで、その処理を決めることが出来ます。

一般的には他の人が作ったシェーダーを利用することが出来ますので、シェーダーについて全てを知る必要はないのですが、
ですが、シェーダー内の大まかな処理を知っておくだけでも負荷軽減に役立ちますので、ここではそれについて説明します。

シェーダーの処理の中には、頂点シェーダーとフラグメントシェーダーという2つの代表的な処理フェーズがあります。処理の順番は、頂点→フラグメントの順で行われます。

頂点というのはメッシュの頂点のことですので、頂点シェーダーにはメッシュの頂点単位でどのような操作を行うかを規定します。

一方でフラグメントというのは画面の画素(ピクセル)を意味しているので、こちらは画素単位でどのような操作を行うかを規定します。

つまり、頂点や画素の一つ一つに対して、頂点シェーダーとフラグメントシェーダーに書かれたプログラムが動いていると考えてください。
当然ながらこのプログラムの処理が複雑なものであるほど、負荷が大きくなります。

また、頂点ごとや画素ごとにプログラムが動きますので、GPUにおけるシェーダーの負荷は頂点数と画素数に依存しています。

頂点数は、3Dモデルの形状が複雑でポリゴン数が多いほど多くなります。ですので頂点シェーダーの負荷は、ポリゴン数に依存していると言えます。

一方で画素数については、プレイヤーの使っているHMDの解像度やその設定、そしてそのオブジェクトが視界に占める領域の割合によって変動します。

ですので、「シェーダーの複雑さ」「頂点数」「画素数」などの要素によってGPUの負荷が決まり、GPUの負荷が高いと絵を描くスピードが遅くなってきてしまいます。これらの要素に気を配ってあげることでGPU負荷を軽量化することが出来ます。

最終的に、CPUから出された全ての描画命令分をGPUが描ききると、やっと1枚の絵が出来ます。これで描画するまでの道のりを終えました。ご苦労様です。

ちょっと長くなってしまって、分かりにくいかと思うので、要点を簡単にまとめてみました。
・CPUは、GPUに命令して絵を描かせている
・命令数が多いとCPUが間に合わなくなる(CPUの負荷)
・GPUは、CPUに貰った情報を元にシェーダーで絵を作る
・シェーダーが複雑だとGPUが間に合わなくなる(GPUの負荷)
・頂点数や画素数が多いとGPUが間に合わなくなる(GPUの負荷)

これらの仕組みを踏まえて、計測することによって、自分がどこを軽量化するべきなのかを認識することが出来ます。

原因を特定して、効率の良い軽量化を行いましょう。

『描画負荷』の計測方法について

それでは『描画負荷』の計測方法についてお話しします。
今回紹介するのは4つあります。

Statistics

まず1つ目がUnity標準機能のStatisticsです。

Unityの再生(プレイ)ボタンを押したときに、自動でゲームビューへと遷移すると思いますが、ゲームビューの右上にStatsの項目があります。非表示の場合は赤矢印の部分を押すと表示できます。
代表的な項目はBatchの総数を表す「Batches」や、SetPass Callの総数を表す「SetPass Calls」などです。
また、視界内のポリゴン数(Tris)や頂点数(Vert)、オブジェクトの影の数(Shadow casters)なども確認することが出来ます。

(補足:TrisとVertの数値に“M”というのが付いていますが、これはミリオン(Million)……すなわち100万を表しているので、手前の数字を100万倍すればOKです。上の画像の場合はポリゴン数が500万で、頂点数が420万になります。"K"が付いている場合は、キロ(kilo)なので1000倍です)

フレームデバッガー(Frame Debugger)

Statisticsは描画命令の総数を知ることはできても、各描画命令の詳細を知ることはできません。この各描画命令1個ずつ確認できる機能としてフレームデバッガーと呼ばれる2つ目の機能があります。こちらもUnityの標準機能です。

大体の項目はどちらかというとシェーダー書く人向けです

軽量化の技法の中にはBatchesとSetpasscallsを減らす技法があります。どうするのかというと、大体の技法が複数回の描画命令を1回の描画命令にまとめることで軽量化します。

ですが、特定の条件下では上手くまとめられないケースがあります。そんな時にどのオブジェクトがどんな理由でまとめられていないのかを、フレームデバッガーでは確認することが出来ます。

上の画像で言うとDetail欄のBatch causeの部分で、この画像の例だと
『異なるリフレクションプローブに影響を受けている』
のでバッチが纏められないみたいですね。

拡大した画像

とはいえ、これは突き詰めて軽量化したい人向けかなと思います。StatsのBatchesとSetpasscallsでも十分な指標になるからです。

ですが、このフレームデバッガーには一度触って見て欲しい、とても面白い機能があります。

ウィンドウの真ん中上部にスライダーバーがあると思いますが、実はこれは全ての描画イベントを順に見ていくことができます。左から右にスライドさせていくと、オブジェクトが描画されているのを見ることができます。
まるでコマ送りのように描画されてる様子を見るのは中々面白いですよ?

Profiler(プロファイラー)

3つ目は同じくUnity標準機能のProfilerです。こちらではCPUやGPUなど各種領域のパフォーマンスを波形で確認することが出来ます。また詳細を見ることできるので、どのオブジェクトが描画される時にどれくらいの負荷をかけているかを確認することが出来ます。

ただし、これはあくまでUnityエディタ上での話ですので、実際のVRChat環境とは色々と違うことを念頭に置く必要があります。

例えばCPU面の負荷で言えば他プレイヤーがいる時のサーバーとの通信が無かったり、GPU面の負荷で言えばHMDでは両目分の映像を作っているのに1つ分しか作られていないだったりするので、これで軽かったからOKというわけでは全くありません。

また、他に気を付ける点として、他の人のCPUやGPUはワールド製作者である自分のパソコンと同様ではないことがありますので、自分の環境では軽くても他の人からは重いワールドになっていることもあります。

つまりこのProfilerのFPSはあくまでも目安でしかないです。

ですが、相対的ではあっても、自分のワールドがどれくらい軽くなったかを視覚的に確認できますので、闇雲に軽量化するよりもはっきりとした指標になってくれるでしょう。

ちなみに下側のHierarchyを上手く使うと、どのオブジェクトの描画にどれくらい負荷がかかっているかを調べることなどができます。
(が、CPUとGPUで内容が違って、説明が長くなって大変なので割愛します)

FPSVR

最後にVRChat内の負荷を確認する方法として、有料のSteamアプリですが『FPSVR』をお勧めしています。このアプリを利用すると、VRChat内でCPUやGPU負荷を確認することが出来るので、プレイヤーが多い場合の負荷なども計測できます。詳細は以下の記事で紹介しています。

基本的には上記の機能やツールを用いて、モニタリングを行いながら軽量化を行っていきます。

各種軽量化技法の簡単な紹介

前述したように『描画』の負荷はCPU負荷、GPUの頂点シェーダーの負荷、GPUのフラグメントシェーダーの負荷に大別できますが、それぞれの負荷に対してどれを使えばいいのかを簡単に紹介していこうと思います。

各技法の簡単な説明は『VRChatのワールド制作基礎能力ツリー』のFPS改善の項目に書いていて、今回の記事に合わせてオススメ度合いも追記したので、そちらを参照してみてください。

Batch数やSetpass Call数といったCPUの描画命令数を抑えるために出来る方法は以下の6つです。
・静的バッチング(Static Batching)
・GPU Instancing
・メッシュ結合
・マテリアル結合
・Occlusion Culling
・LOD

頂点シェーダーの負荷を減らす方法としては以下の3つがあります。
・ポリゴン削減
・Occlusion Culling
・LOD

フラグメントシェーダーの負荷を減らす方法ですが、残念ながらこちらはシェーダー処理の効率化以外ないと思います。
ですので、シェーダーに触れたくないのであれば、やりたい表現と負荷を天秤にかけて、負荷を許容するのか軽量なシェーダーに切り替えるのかになると思います。

最後に私たちが何もしなくても、Unityで自動で行ってくれている軽量化技法について説明します。
・動的バッチング(Dynamic Batching)
自動でCPU側でメッシュをまとめる機能ですが、とても厳しい条件があります。Batch数を抑えるための技法です。
・視錐台カリング(フラスタムカリング)
視界外のオブジェクト(カメラのNearより手前、Farより奥も含む)を描画しない仕組みです。この仕組みのおかげで視界外のオブジェクトの描画負荷はほぼ0です。ただし、視界内と視界外にまたがっているオブジェクトには適応されません。
そのため広いワールドとかで全てのメッシュを結合してしまうと、この技法の恩恵を受けられなくなってしまいます。視界外の部分はフラグメントシェーダーは実行されませんが、頂点シェーダーは全頂点分実施されます。
ですので、適度にメッシュは分割しておいて、この技法の恩恵を受けられるようにしておきましょう。

『描画』以外のCPU負荷について

勿論『描画』以外に割かれる負荷というのもあります。それについては賢いCPUが対処することになります。

例えば、
・VRChatのサーバーとの通信や同期
・Udonのギミック
・Unityのコンポーネント(特にAnimatorや物理演算、uGUI等)
などは、CPUにかかる負荷がメインになります。特に他のプレイヤーがいる時は通信が頻繁に行われて非常にCPU負荷が高くなるので、batch数やSetpass Call数は極力抑える必要があるでしょう。

Profilerを使って負荷を減らそう


先ほど描画負荷の計測方法として4つ紹介しましたが、その中で一番キチンと使ったほうが良いとオススメするのがProfilerになります。

とはいえ、微妙にとっつきにくかったり、分かりにくいところがあったりするので、使い方を簡単に紹介したいと思います。

まずWindows→AnalysisからProfilerを選択して、Profilerのウィンドウを出します。

ワールドであれば、とりあえずCPUとGPU負荷が見れればよいので、左上Profiler ModulesからCPUとGPUを選択しておきましょう。

CPUとGPUのモニタリング画面左側に項目がありますが、その文字の左側にある色付きのボタンを押すと、その項目のモニタリング結果を除外できます。

CPUはVSyncを、GPUはOthersを消しておくと良いと思います。

CPUのVSyncは、GPUのほうの負荷がCPUより高い場合に、GPUの処理が終わるのを待っている時間を示しています。要は負荷とは関係ないため消してもよいと思います。

GPUのOthersはほぼProfilerによる計測負荷のため、基本的には消しておいて大丈夫です。
CPUのOthersもProfilerによる計測負荷が入っているので消したいですが、一部のUdonの負荷はここに入ってくるので消さないほうがいいでしょう。
(余談:Unity2022になってスタンドアローンのProfilerが追加されて、Profiler自身の計測負荷を除外できるようになりました……が、どうせ目安でしかないので、自分は既存ので十分だなと思って使ってません)

次にプレイ(再生)ボタンを押し、ゲーム画面を表示した状態でモニタリング結果が安定するまで放置します。

この計測時、Unity以外のアプリは計測の邪魔をするので、基本的には落とせるものは全て落としておきましょう(他のアプリの負荷は見えないので、FPSが以前よりも下がったように錯覚させられます)

シーン画面を表示していると、シーン画面で見えてる部分の負荷になってしまうので、基本はゲーム画面にしておいてください。加えて、理由は分かりませんが、Reflection Probeなどを選択していると、Profilerが安定しないので、Scene名などを選択しておくと良いです。

ゲーム画面の左側上方に、デフォルトだとFree Aspectとなっている部分がありますが、これはゲーム画面の解像度です。

フラグメントシェーダーの負荷を適切に調べるために、ここの解像度を重ための数値に設定しておくとグラフが見やすくなって良いです。VRChat内でのFPSと一致するように、解像度を設定してあげてもいいでしょう。
(補足:負荷に応じてグラフが勝手に拡大・縮小するのですが、解像度が低い状態だと負荷がとても小さく各オブジェクトの影響度が分かりにくいので、解像度を上げます。固定する方法知っている人居たら、こっそり教えてください)

ここから二つの方法に分かれます。
一つはオブジェクトをオンオフして、負荷がガクッと減るやつを探し当てる脳筋的手法。
もう一つはProfilerの機能であるHierarchyを利用して、負荷の高いオブジェクトを探し当てる手法。

今回は初心者向けなので、前者について説明しましょう。後者は自分で調べてみてください。

とはいえ、やり方は簡単で、プレイ状態のまま、オブジェクトを片っ端からオンオフしていくだけです。オフにするとその分の負荷が消えるので、波形が小さくなります。

このようにガクッと減ります

疑わしいのがあれば、真っ先にそれをオフにしても良いです。ですが、心当たりが無ければ半分とかエリアごととか、纏まった単位で複数選択してオフにして、絞り込むといいと思います。

大規模になればなるほど、ざっくりしたやり方も必要になってくると思います。覚えておいて損はないはずです。
(補足:大丈夫だと思いますが念のため。オブジェクトの複数選択は1つ選択した状態で、ここまで選択したいってところで[Shift]+左クリックするか、または[Shift]を押しながら[↓]キーを連打するかです。その状態でオブジェクトをオフにする場所を押すとまとめてオフにできます)

追記:私はあまりそういうワールド作らないので忘れていたのですが、Udonとかでプログラミングモリモリのワールドを作っている人は、上に書いたような脳筋な使い方ではなく、Hierarchyとかを活用する丁寧なProfilerの使い方が必要になってくると思います。
良い記事を思い出したので貼っておきます。

Unityのことに関して検索すると大体ぶち当たるテラシュールブログさんの記事です。ありがたい……。特にVRChatからプログラミングを始めた人はGC Allocの話とか頭の片隅に入れといたほうが良いと思います。
Unityが用意してくれてる関数も重たいのがあったりするので、そこら辺も余裕があったら調べてみるといいでしょう。例えばGameObject.Findとかは有名ですよね。

おわりに

負荷軽減というのは、仕組みを知ったうえでようやく効率的に行えるものだと思います。ですが、初心者にとって、負荷は見えない巨人のように立ちふさがってきます。

その巨人の正体を暴くための方法を提供しようと思って書き始めたのですが、結構難しい内容になってしまったかもしれません。

最初は手探りでもいいと思います。
理解というのは相互的に進んでいくものなので、ちょっとずつでも軽量化を進めているうちに、いずれ点が線になり、線が面になり……と今回説明した内容への理解が深まっていくでしょう。

ちょっとした雑談をすると、私は大きいワールドが好きなのですが、大きいワールドにはどうしても負荷軽減が欠かせません。
この記事を出したことで、大きいワールドが増えたら嬉しいな~と思っています。

さて、ここまで読んでくれた真面目な貴方に敬意を表しつつ、この文章を締めさせていただこうと思います。長文読了、お疲れ様でした。


コラム

上の文章に入れてしまうと、ただでさえ初心者向け?みたいな感じなのに、さらに初心者向けじゃなくなるなと思った内容をこちらにまとめてみました。

半透明の描画負荷について

半透明のオブジェクトを多用している時は、フラグメントシェーダーの負荷に注意を払う必要があります。

詳しくは『半透明の記事』にまとめているので一度目を通してほしいですが、かいつまんで言うと絵でいうところの重ね塗りをしています。

半透明のオブジェクトは奥のオブジェクトが透けて見える状態なので、奥のオブジェクトの描画結果と手前のオブジェクトの描画結果を混ぜ合わせる必要があります。
そのため、画素の塗り直しが起こります。つまりこれは手前と奥の2つのオブジェクトのフラグメントシェーダーが別々に実行されるということを意味しています。

GPUには1秒間に描画できる画素としてフィルレートという指標があります。何が言いたいかというとGPUが1秒間に描画できる画素数には限界があるということです。

もし過剰に塗り直しが行われて、その限界を超えてしまえば、GPU由来のFPSの遅延が発生していきます。

ですので、半透明のオブジェクトを使用する場合は注意したほうが良いでしょう。具体例を挙げると、沢山の半透明パーティクルは沢山重なって描画されてしまう筆頭でしょう。

リアルタイムシャドウの描画負荷について

リアルタイムライトの影についてもフラグメントシェーダー単位で処理が行われます。そのため以下のような取り組みが必要になります。
・影付きリアルタイムの光源を少なくする
・静止している光源はライトベイクする
・影が必要ないオブジェクトはCast ShadowやReceiveShadowを切る
・レイヤーを分けて、照らす必要のないオブジェクトに光を当てない
・影の解像度を下げる
・LightコンポーネントのRenderModeをNot Importantにする
補足:下の二つは影の品質を下げるので、見た目と相談してください。Not Importantにすると処理方法が変わり、簡単に言うと画素単位から、頂点単位のライティングになって軽量になりますが、裏を返すと均等に頂点が無いと破綻します。

極力リアルタイムの光源は避けたほうが良いです。
シェーダー内部の処理など詳しい仕組みは割愛しますが、簡単に言うとRenderTextureのように光源にカメラを置いて、深さ情報のシャドウマップと呼ばれるテクスチャを作っています。

つまるところ、リアルタイムの光源一つごとに毎フレーム絵を作っているわけです。とりわけヤバいのがポイントライトで、生成するテクスチャがキューブマップなので上下左右前後6枚とって合成しています。ヤバいですね。
(補足:ディレクショナルとスポットは1枚)

あと、異なる複数のライトはバッチ条件を壊してしまうので、Batch数が増えやすいです。
(余談:ディレクショナルライトを動かしている私のワールド"Everlasting Days"だと、Batch数が少ないときは300ちょっとですが、夕方の日が差し込んでくるあたりで800近くまで増加したりします)

経験則上、影の解像度を下げるのはかなり有効的です。どうしても使わないといけない場合は、LightコンポーネントのRealtime ShadowのResolusionを最も低いLowにするなど解像度の調整をすると良いでしょう。

ポストプロセスの描画負荷について

オブジェクトの描画が一通り終わった後に、画面全体の画素に対して実施されます(ポストプロセスの文字通り”後処理”なわけです)
ですので、無いほうがGPU負荷は軽くなります。

勿論、使うエフェクトによって、負荷が増減します。

私はBloomとColor Gradingくらいしか使いませんが、あまり重くはない負荷で、良い効果が得られるので、特に気にせずにポストプロセスを使っています。
(余談:Profilerで計測項目が"PostProcess"と別枠になっているので分かりやすい)

バッチ処理の中断理由

フレームデバッガーのところで「バッチが中断される理由が書いてる」という話をしましたが、実はバッチされない理由は結構あります。

それをまとめたサンプルプロジェクトがGithubで公開されていたりします。
理由一覧が説明に書いてあるので、興味があったら見てみるといいと思います。

頂点シェーダーとフラグメントシェーダーの負荷の比重

これはちょっとした小話なのであんまり覚えとく必要はありませんが、頂点シェーダーとフラグメントシェーダーどちらのほうが重いか?という話ですが、一般的にはフラグメントシェーダーのほうが重いとされています。

理由は単純で実行回数がフラグメントシェーダーのほうが多いからです。

頂点シェーダーは頂点ごとに、フラグメントシェーダーは画素ごとに実行されるという話をしたと思います。

一般的に頂点は適切なモデルであれば多くて10万ちょっとくらいってところですが、画素はQuest2のフル解像度を例に挙げると3664×1920≒703万と非常に多いので、負荷としてはフラグメントシェーダーのほうが厳しいです

ですので、シェーダーの軽量化の話になると、フラグメントシェーダーで行っている処理を頂点シェーダーで行っておく、という話が出てきます。

とはいえ、頂点シェーダーの負荷を軽く見てはいけません。頂点数はモデル次第でいくらでも増やせますし、フラグメントシェーダーと違って遮蔽されていたり、視界外にまたがっている場合でも実行されてしまうからです。

削れるところは削るに越したことはありません。

レンダリングの流れ

今回説明した『描画』の流れの部分では、負荷にあまり関係ない部分のお話はあえてしませんでしたが、そこら辺を以下の動画やスライドで補充することができます。

スライドの33ページとかはまさに今回の流れを見通せると思います。


おまけ
サムネイルに悩んで、ChatGPT君に相談という名のダル絡みをしてたんですが、面白いサムネイルを出してきて、思わずしばらく笑ってました。
流石に印象がアレだなと思って使いませんでしたが、面白かったのでここに供養しておきます。

VRゴーグルをかけたアバターが、「重い」象徴(例: 重量挙げのバーベル)から「軽い」象徴(例: 羽根)へと移行する様子


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