BlenderとUnityで作ったものをVR(Meta Quest/Oculus Link)で動かします

ブログの女の子を作る #103 Meta Quest 2+Oculus Linkでフレームレートが半分になる?なんとか動的解像度で36→72FPSに改善できました【Unity】

 
前回はビジュアルスクリプティングで、オブジェクトを触るとTimeline実行する仕組みを作ってみました。
 
作ったモノを動作確認する際、Meta Quest 2+Oculus Linkでつないでいたのですが、全体的にフレームレートが低い感じです。Unityエディター上(ビルド前)で 36FPS 前後、ビルド後は 45FPS 程度でした。つないでいない時は 60FPS 出てたので、やはりVR時は負荷が重いようです。片目だけで解像度が 1,832×1,920 ありますし。
 
Meta Quest 2 のリフレッシュレートは 72Hz なので、72FPS を目指してフレームレート改善を頑張ってみました!
動的解像度を使って目標は達成できましたが、若干解像度が下がってしまったのが残念です。
 
一つの区切りとして、これまでに試したことを記事にまとめてみました。


開発環境

・Blender 3.0.0
・Unity 2021.3.0f1
・CPU:AMD Ryzen 7 3700X
・グラボ:ASUS ROG-STRIX-RTX2060S-O8G-GAMING
 

(1) フレームレート改善の結果です

結果を先に書いておくとこんな感じです。
 
  1. FPSがリフレッシュレートの半分(72Hz なら 36FPS)で安定してしまう
    そんなに負荷が高くないのに、40 とか 50FPSでなく 36FPS になる。なぜ?
  2. ライトの数を減らす、オクルージョンカリングなどは実施済み
    今回の Scene では大きな効果はありませんでした。
  3. 描画解像度を下げるのが一番効果あった
    固定の場合は eyeTextureResolutionScale、動的にする場合は「動的解像度」を使います。
    一般ゲームでも解像度下げるとフレームレート下がるので、効果があるのは分かってましたが最終手段ですね。
動的解像度は便利なんですが、解像度が下がり過ぎるとドット感が気になるので、欲しいフレームレートとのトレードオフになります。
本当は、通常の解像度のままで 72FPS になると良かったんですけどね。
また次回の改善ということで。
 


(2) 試したことまとめ

フレームレート向上、処理の最適化を目指して試したことです。
FPSを確認するのは大事ですね。
 

1. テクスチャを圧縮する

テクスチャファイルの Inspector で Use Crunch Compression にチェックを入れます。
 
これは処理負荷軽減というよりは、ロード時の時間短縮ですね。
実際、FPS改善には効果ありませんでしたが、ビルド時間の短縮にもなりそうなのでやっておきました。
 

2. 不要なポストプロセスをオフにする

とりあえず、Contact ShadowsBloom をオフにしました。
影処理(Shadows、Ambient Occlusion)は見た目がかなり変わりますので、オンのままです。
 

3. プロファイラーで負荷を確認する

Unity標準のプロファイラーを使うと、CPUやGPU、そしてメモリなどの処理負荷を確認できます。
 
Unity公式の動画も参考にさせていただきました。
 

4. オブジェクトとライトを消すと負荷が下がる

当たり前ですが、オブジェクトとライトを消すと負荷が下がります。
 
全部ありの場合、32.8 FPS。
 
部屋アセットとライトを削除すると、71.7 FPS になります。倍ですね。
もちろん、この状態では意味は無いのですが、差が出ることを確認しておきたかったので。
 

5. オクルージョンカリングが効いてるか確認する

オクルージョンカリングは「カメラに見えていない部分」を描画しないことで負荷を下げる仕組みです。設定しているつもりですが、念のため確認しておきます。
 
リビングから上を見た場合、キャラクターと周りの家具だけが表示されてます。
 
更にカメラで右側を見てみると、キャラクターは消えて、代わりにキッチン周りの家具が表示されます。
オクルージョンカリングは問題なく動作してますね。
 
ちなみにですが「部屋の壁」は一つの大きなオブジェクトなので、常に表示されてます。これをメッシュ分割して小さなオブジェクトにしても良いのですが、あまり効果は無い気がしてます。ポリゴン数も少なそうですし。
 

6. 固定中心窩レンダリングは使えない?

固定中心窩レンダリングを使うと「視野の周り部分」を粗く表示して、負荷を下げることができます。
OVRManager.fixedFoveatedRenderingLevel = OVRManager.FixedFoveatedRenderingLevel.High;
 
ですが、今回試したところ使えませんでした。以前は使えた記憶があるのですが。。。
 
Console に「Fixed Foveated Rendering feature is not supported」と表示されてるので、何かが対応してないっぽい。Oculus Link でなく、Meta Quest アプリで無いとダメなんでしょうか。残念。
 

7. ライトのモードを変えてみる

今回使っている「部屋のアセット」を確認してみると、意外にたくさんのライトが使われてました。
ライトは「Baked → Mixed → Realtime」の順で負荷が高くなっていくので、Realtimeライトは少ない方が良いですね。
 
とりあえず、全部 Baked に変更してみましょう。
 
影の見た目が変わり、少しアニメ調になりました。
肝心の負荷ですが、35.9 FPS で変わりませんでした。
少しは負荷が下がるはずなんですけどね。。。
 

8. 36FPSの次は72FPS?

今回の検証でおかしいと思ってるのが、「FPSがほぼ 36 になる」ということ。
 
・負荷がある程度かかっている状態 → 36 FPSで安定してしまう
・すごく負荷が低い状態 → やっと 72 FPS になる
なんですよね。
 
実際に試してみます。
ビルド時+Oculus Link の場合、通常時で 35.8 FPS でした。
 
部屋から出て描画負荷が下がると、一気に倍の 71.7 FPSになりました。
なぜ??
 
普通であれば、負荷によって 1から72までのFPS になると思うのですが。
わからん。。。
 

9. VSyncが関係している?

Unityの設定で VSync(垂直同期)があります。
Project Settings -> Quality -> Rendering -> VSync Count」で設定できます。
デフォルト値は「Every V Blank」で、フレームレートができるだけ高くなるようUnity側で調整してくれます。
 
Unityの説明によると、これを「Every Second V Blank」に変更すると、フレームレートがリフレッシュレートの半分になるようです。「半分」というキーワードはありますが、これは「常に半分」になるらしいので、今回の現象(常時は半分だが、負荷が下がるとFPSは半分を超える)とは違うようですね。
 
そもそも、設定してるのは「Every V Blank」なので、半分になるはずはありませんし。
 

10. Fixed Timestep を変えて見る?

Time -> Fixed Timestep」を 0.01 → 0.01388889( = 1 / 72 FPS) に変更してみました。
 
この設定は「フレームレートに依存しないで、物理計算や FixedUpdate() イベントを実行する間隔」ですので、あまりFPSには関係ないですが、「表示するFPSと数値を合わせた方が良い。合わせないと表示がガクガクする場合がある。」と聞きましてので、設定しておきました。
 

11. 壁と天井を消してみる

意味はありませんが、試しに壁と天井を消してみます。
 
すると、FPS が 41.3 に上がりました?
 
60 FPSを越える場合もあります。
大したオブジェクト数では無いと思うのですが、なぜか大きくFPSが変わりました。
とはいえ、消すわけにもいきませんので、表示しておきましょうか。
なぜ?
 

12. カメラが天井を越えると72FPSになる?

FPSが一気に変わる、分かりやすい場所がありました。
 
室内では 35.9 FPS ですが、
 
カメラを上に移動して、天井を越えると 72.2 FPS になりました。
 
Statistics で描画内容を確認してみます。
・Batches:625 → 518
・Saved by batching:182 → 152
・Tris:1.2M → 1.1M
・Verts:1.0M → 918.2k
・SetPass calls:235 → 229
・Shadow casters:347 → 344
 
「室内 → 天井越え」でも、数値的には大きな変化はありませんが、FPSは倍になっています。
なんかこのあたりにも、フレーム低下の原因が隠されてる気がします。
 

13. 部屋内のオブジェクトを消してみる

部屋内にはたくさんのオブジェクトがありますのでそれを消してみます。
FPSは 36.0で変わりませんでした。
壁や天井よりは、メッシュ数も多いと思うんですけどね。
 

14. シェーダーを変えてみる

シェーダーで重そうな処理をオフにしてみます。
 
まずは通常時。設定変更前です。
 
ここで、キャラクターには HDRP/Lit の Subsurface Scattering を多用してたのでオフにしてみます。
かなり見た目が変わってしまいましたが、36.0FPSと同じでした。
 

15. Oculusアプリ側でリフレッシュレートを90Hzに変更する

PC側のOculusアプリで、リフレッシュレートを 72Hz → 90Hz に変更してみます。
 
すると、FPSが 45.0 になりました!
9フレームの向上です。
 
そして負荷が低い「天井の上」にカメラを移動すると、80 FPSを越えてます。
 
90Hz に変更した時に、画面解像度も少し小さくなったので負荷が下がった可能性もありますが。。。
 

16. サーマルスロットリング機能を疑う

Meta Quest にはサーマルスロットリングという機能があり、本体の温度が高くなり過ぎた場合、一時的に処理速度を落とすことでデバイスをクールダウンしてくれます。
 

80 Hzまたは90 Hzで動作するQuest2アプリで過熱イベントが発生すると、動的スロットリングの最初のステップとしてリフレッシュレートが72 Hzに下げられることがあります。過熱状況が悪化すると、動的スロットリングの次のステップが実行され、リフレッシュレートを72 Hzに維持しつつフレームレートが36 FPS(minVsyncs=2に相当)に下げられることがあります。

ここにも「半分(72 → 36)」の記載がありますね。
 
サイトの記載通り、リフレッシュレート変更イベントを検出する処理を追加してみます。
OVRManager.DisplayRefreshRateChanged += DisplayRefreshRateChanged
 
ついでに、変更イベントが発生した場合、画面上に「変更後のリフレッシュレート」を表示するよう実装追加します。
private void DisplayRefreshRateChanged (float fromRefreshRate, float ToRefreshRate)
{
    textMeshPro2.SetText( string.Format("Refresh rate changed from {0} to {1}", fromRefreshRate, ToRefreshRate) );
}
 
この状態でしばらく使ってみましたが、変更イベントは発生しませんでした。
 
念のため、Meta Quest の電源を切って一日放置して、完全に冷めた状態で試してみましたが結果は変わらず。「温度上昇が原因でフレームレートが半分になった」では無さそうです。
 

17. 視野全体の解像度を0.9に下げてみる

eyeTextureResolutionScale の数値を 1.0 から下げると、視野全体の解像度を下げることができます。
UnityEngine.XR.XRSettings.eyeTextureResolutionScale = 0.8f;
 
とりあえず、0.8f にしてみました。
いきなり 90.0 FPS ですね。
 
分かってはいましたが、画面解像度を下げるのは効果がありますね。
ドットが粗くなるのがネックですが。。。
 

18. 視野全体の解像度を細かく調整してみる

どこに閾値があるか試してみます。
 
「eyeTextureResolutionScale = 0.95f」の場合、36.0 FPSです。
 
「eyeTextureResolutionScale = 0.9f」の場合、68.0 FPSに上がりました。
 
ですが、踊るモーションを実行して負荷があがると 36.0 FPSになりました。
0.9f くらいなら、ギリ許せる気がします。
なにより、負荷が低い状態でやっと 36.0 FPS を越えたのはうれしい。
 

19. Unityアプリのウィンドウ解像度を下げてみる

Unityアプリをビルドする際、Player設定で「アプリの解像度」を設定できます。
 
Oculus Link で使う場合、Windows側の画面にも描画してますので、その解像度を下げれば負荷も下がるかも?という考えです。
 
Default Screen Width と Default Screen Height ですね。
かなり小さくしてみましたが、FPSは変わらず。恐らくですが、「Meta Quest で描画する画像データはここの設定とは関係ない。Oculusアプリで設定した解像度が負荷に影響する」ということなんでしょうね。
 

20. WindowsとNVIDIAの設定を確認する

PC側の設定を確認しておきます。
 
電源オプションは、既に「高パフォーマンス」になってました。
 
電源オプションの詳細設定で、「プロセッサの電源管理」も 100% です。
 
次は、NVIDIAコントロールパネルです。
 
「低遅延モード」とありますが、これは「コントローラなど入力装置の遅延」のようですね。
FPSとは関係ないと思いますが、とりあえず「ウルトラ」で。
 
一番関係ありそうな「電源管理モード」は「最適電力」でしたので、「パフォーマンス最大化を優先」に変更してみます。
 
 
少し期待しましたが、結果は変わらず。
 
この状態でGPU負荷は 68% でした。
そこそこ使っているようですね。
 

21. 動的解像度を使う

HDRPでも動的解像度が使えるようなので試してみました。
 
変更箇所は2か所。
 
まずは、HDRPの設定用アセット(今回はHDRenderPipelineAsset)の Hierarchy で、「Dynamic resolution -> Enable」と「Enable DLSS」の両方にチェックを入れます。
 
次は、カメラのアセット(今回はCenterEyeAnchor)の Hierarchy で、「Rendering -> Allow Dynamic Resolution」と「Allow DLSS」の両方にチェックを入れます。
 
Oculusアプリの設定は 72Hz に戻しておきました。
 
この状態で実行すると、71.6 FPSになりました!
 
もちろん「動的解像度」なので、負荷が高くなるとFPSを維持する代わりに、解像度が下がっていきます。
解像度は少しずつ変化するので変わり目はほとんど分かりませんが、遠くのオブジェクトのドットが粗くなったり、オブジェクトのフチにジャギーが見えたりするのでやや気になります。とはいえ、オブジェクトに近づいた時はほとんど気にならないレベルですので、使い方によっては良いかも知れませんね。
 
気になる点が2点ほどありました。
・HDRPアセット側の設定「Minimum Screen Percentage」と「Maximum Screen Percentage」で、どこまで解像度を下げるかを設定できるのですが、今回は使えませんでした。これを使うと「10%まで解像度減を許す」的なことができるのですが。
・Unityの説明では「動的解像度は自動機能ではありません。手動で SetDynamicResScaler() 関数を呼び出す必要があります。」的なことが書いてありました。今回、この関数は呼んでませんが正しく動いてる感じです。
とりあえず、使えているのでOKとしましょうか。
 
 

(3) フレームレート改善を動画で確認する

動画で確認してみましょう。
 

改善前の動画

まずは、前回の動画です。
特に「踊るモーション」のところがカクカクしてますね。
 

改善後の動画

次に、今回フレームワークを改善した状態での動画です。
最初から最後まで、72FPS を保ってますね。
 


まとめ

「Meta Quest 2+Oculus Linkでフレームレートが半分になる」現象が起きていましたが、動的解像度を使うことで 36 → 72FPS に改善できました。
 
Meta Quest経由で見ると、解像度低下が若干気になりますが、キャラクターに近づいた時はほぼ分かりません。
当面はこれで使うことにしましょう。
 
スポンサーリンク

コメント

  • トラックバックは利用できません。

  • コメント (0)

  1. この記事へのコメントはありません。

ブログの女の子を作る #102 ビジュアルスクリプティングで「オブジェクトを触るとTimeline実行」を作る【Unity】

ブログの女の子を作る #104 顔の影を法線転写とSSS(サブサーフェス・スキャタリング)で軽減する【Blender,Unity】


最近のコメント

だーしゅ
IT関係のお仕事してます。
3Dモデルの女の子は「ブログノ・スージー」。VRは楽しいですね。

[当ブログについて]