皆さま、こんにちは。Cygames Researchの金井と申します。
Cygames Researchでは研究開発の対象としてVRコンテンツを取り扱っていますが、今回はその一例として、The Lab Renderer for Unityの検証内容についてご紹介したいと思います。
The Lab Renderer for Unityとは
The Lab Renderer for Unity(以下Lab)は、Valve社から提供されているUnity向けのAssetsであり、Unity上でVR開発を行う際の最適化手法が収められています。このAssetsは、Unity5.4 b15以降から利用可能であり、こちらのURLに概要の紹介があります。
最適化の内容についてはGDC2016でも一部紹介がありましたが、このAssetsからもValve社のVR開発における最適化の考え方を汲み取ることができ、非常に参考になる内容となっています。また機能の多くはUnityの標準的な機能で作成されており、OpenVRだけでなく、他のSDK(Oculusなど)でも利用可能です。今回は、このLabの内容についてより深く掘り下げたいと思います。
まずは使い方
使い方は非常にシンプルです。
- まずはThe Lab Renderer for Unityを入手します。こちらからAssetsをダウンロード、対象のプロジェクトにインストールします。
- ダイアログが表示された場合、表示されている内容に従って、Project Setting等の変更を行います。
- 対象シーンのカメラにValveCameraコンポーネントをアタッチします。
- 対象シーンのライトにValveRealtimeLightコンポーネントをアタッチします。
- 対象シーンのメッシュが利用しているシェーダーをValve/vr_standardに置き換えます。
これで、Valve社が提供しているシャドウマップ最適化や適応型品質が利用できるようになります。
Labの特徴
英語となりますが、使い方についてはこちらの資料に記載がありますので、まずは一読をお勧めします。Labでは、VRへの最適化として以下が行われています。
Single-Pass Forward Rendering and MSAA
前述の資料やLabに含まれるサンプルシーンを見る限り、Valve社は昨今のVRではSingle-PassでのForwardレンダリングにおけるMSAAが重要だと考えている模様です。VR体験の向上においては画面解像度が重要となりますが、解像度にはハードウェア的な上限があるため、MSAAx4、可能であればMSAAx8を適応したいところです。しかしながら、MSAAx8となるとGPU負荷もそれ相応となります。LabではCPUおよびGPU負荷の改善のため、シャドウマップ生成に改善を加えています。UnityのForwardレンダリングはスポットライトおよびポイントライトに無駄が多く発生しており、このボトルネックを改善するためのアプローチとなります。
より具体的に説明すると、Unity標準ではシャドウマップテクスチャはライト数に応じて複数用意され、メッシュに対する影を処理する場合、同一メッシュが複数回描画されます。しかしながらこれはDrawCallの増大を招き、テクスチャが複数に分かれるためSetPass callsの削減も期待できません。Labではシーン中にシャドウマップテクスチャが1つ用意され、それを各リージョンに分割し、各ライトが利用することで、複数パスでの処理を回避しています。
サンプルシーンにて計測してみました。非Single-passでの計測ですが、ポイントライトを3つ配置したシーンに複数のMeshを置いたところ、Batch数は4259になりました。
このシーンにおけるMeshのシェーダーを前述の方法で置き換えると、Batch数は2011となります。
この手法は、OpenVR専用の最適化ではなく、例えばOculus SDKでも動作します。そして最適化の効果、すなわちBatch数の削減が確認できるはずです。Labで適応された最適化はUnityに準じる形で行われているため、シェーダーモデル等の固有の問題を除けば、原理的にはどのSDKでも動作します。
Adaptive quality
GDC2016で提案された最適化手法で、画面解像度をGPU負荷に応じて変動させ、フレームレートを維持します。VRにおける体験を向上させるには、高いフレームレートでの安定動作が求められるため、GPU負荷に応じてレンダリング解像度を下げることで、フレームレートの維持を試みます。この手法は、スペックが一様とはならないPC環境において、特に有用であると考えられます。
LabではVRStats.gpuTimeLastFrameとVRSettings.renderViewportScaleを用いてこれを実現しています。これらの機能は、Unity5.4より追加された要素となります。
VRStats.gpuTimeLastFrameを利用することで、Unity上でも直接的にGPU負荷を取得することができ、直前フレームにおけるGPU負荷時間がわかります。ただSDKによってはこの値は0が返り、例えばOculus SDKでは0が返ります(7/7現在)。0が返る場合、SDK側にてまだ対応が行われていない、という事になります。
VRSettings.renderViewportScaleを用いることで、レンダリング解像度を仮想的に変更することが可能であり、比率を0.0~1.0で指定することができます。同様の機能にVRSettings.renderScaleがありますが、こちらを変更すると解像度が物理的に変更されるため、メモリの再確保が入ります。renderViewportScaleは仮想的な解像度変更となるため、再確保が入りません。動作時にRenderDocでキャプチャを行うと、以下のようにレンダリング領域が縮小されている事がわかります。
余談ですが、RenderDocは描画の詳細情報を手軽に取得できる良い手段であり、Unityの内部動作を予測するにあたり非常に有用です。詳細はこちらにも記載があります。
Custom Shaders
前述のシャドウマップ最適化等を行うため、Valve社独自のStandardシェーダーが用意されています。合わせて、シーン中やプロジェクト中のマテリアルを一括置換するためのツール機能も提供されているため、既存プロジェクトにて移行作業を行うことも比較的容易になります。
カスタムシェーダーは、以下のBuild-Inシェーダーと互換性があり、最低限の物が網羅されています。
- Standard
- Standard (Specular Setup)
- Unlit->Color
- Unlit->Texture
- Unlit->Transparent
- Unlit->Transparent Cutout
またシェーダーコードはすべてAssetsに含まれる形で公開されているため、これをベースにタイトル固有の拡張を行う事も可能です。
GPU Flushing
VR開発のTipsとして、GPUのbubbleをなくす(何もしていない無駄な隙間)がありますが、それを無くすためにGL.Flushを意図的に呼び出すことが提案されています。今回の検証ではあまり効果が見られなかったのですが、メンバ変数によるOn/Offの制御や、アプリケーション起動時のコマンドライン引数等でも制御が可能なため、プロジェクトに応じて設定を変更することも可能です。
ご紹介は以上となります。
本記事の執筆時点ではUnity5.4は未だベータ扱いであり、Single-Passレンダリングに問題が見受けられるため導入に際してはプロジェクト側での判断が必要となりますが、Labの考え方は非常に参考になるかと思います。VRコンテンツの開発においては、パフォーマンスと品質のバランスを取るのが難しく苦労が絶えませんが、今回の記事が皆さんのお役に立てば幸いです。