VR の Scene 遷移処理中に Compositor Layer を用いた Loading 画面を表示する
VR空間での画面切替は厄介で,突然画面が切り替わると,人は不快に感じてVR酔いに繋がります. VR上で画面が切り替わるときは,映像に連続性をもたせて段階的に画面を切り替えるような処理が必要になります.
対策としては,
- フェードアウト/フェードインを利用する
- 周りを黒くする&視界を狭くする
などが挙げられます.今回は,OVR Overlayを用いてVR空間でSceneを切り替える時に Loading...画面を表示して,ユーザーエクスペリエンス向上とVR酔いを防ごうと思います.
OVROverlay
Oculus Intergration の SampleFramework に OVR Overlay のサンプルシーンがあります. OVROverlay は Compositor Layer を利用できるスクリプトです.
Compositor Layer
VRアプリにおいて、ローディング画面などでレンダリングがリフレッシュレートに間に合わず、 Asynchronous Timewarp (ATW)処理で擬似的に生成されたフレームが表示されることがあります。 このような時、ATWが前の絵の位置をずらしているため、ずれた分だけ表示領域の端に黒い枠が見えてきます。
VRをやっている人ならわかる現象だと思います.アレって没入感が失われるんですよね.
「Compositor Layer 」はOculus SDKに内蔵されている、一部のオブジェクトを通常のレンダリングと別枠で、 出力直前に合成して表示することができる機能です。 レンダリングのフレームレートではなくコンポジターのフレームレートで動作するため、 通常のレンダリングが間に合っていない時であっても常時滑らかに表示されます。
アプリのフレームレートでレンダリングされるのではなく,
Compositor のフレームレート(HMDのリフレッシュレート)でレンダリングされるようです.
これにより,Compositor Layer は表示の揺れが発生しにくく、テクスチャーやテキストがよりはっきりと表示されます.
実装
Hierarchy 構造
適当なシーンを2つ作ります. OVROverlayのサンプルシーンを真似して,下図のようにHierarchyでオブジェクトを作ります. 全て空オブジェクトで作ってます.
Overlay_Background
Overlay_BackgroundにOVROverlayスクリプトをアタッチします.
Overlay ShapeはCubemapにし,Left Textureにskybox02_openGLを入れます.
このテスクチャはOculus IntegrationのSample FrameworkをImportしていると入っているはずです.
Left Textureにテクスチャも入れるとデフォルトではRight TextureにはLeftに入れたものと同じになります.
最初にOverlayを表示したくないため,コンポーネントのチェックを外していることに注意してください.
Overlay_LoadingText
同様にして,Overlay_LoadingTextにOVROverlayスクリプトをアタッチします. Overlay ShapeはQuadにし,Left TextureにLoadingを入れます. 同じくこのテクスチャはSample FrameworkをImportしていると入っているはずです.
同様に,コンポーネントのチェックを外していることに注意してください.
他の設定が具体的になんなのか気になった場合は,以下を詳細してください.
テクスチャは自分の好みで変えても構いません.
SceneLoader
シーン切替用のSingletonクラス,SceneLoaderを作成します. 作成後,SceneLoaderオブジェクトにアタッチし,下図のようにオブジェクトを当てはめます.
シーン切替を行いたくなったところに,SceneLoader.Instance.LoadScene()を呼び出してください.
using UnityEngine; using UnityEngine.SceneManagement; // SceneManager 利用に必要 using UniRx.Async; // UniTask 利用に必要 public class SceneLoader : MonoBehaviour { /// <summary> /// 背景用のOverlay /// </summary> [SerializeField] private OVROverlay _overlay_Background; /// <summary> /// LOADING表示用のOverlay /// </summary> [SerializeField] private OVROverlay _overlay_LoadingText; private static readonly string CENTER_EYE_ANCHOR = "CenterEyeAnchor"; private static readonly int MARGIN_MILLISECOND = 3000; private static SceneLoader _instance; public static SceneLoader Instance; private void Awake() { if (Instance != null && Instance != this) { Destroy(this.gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); } public async void LoadScene(string sceneName) { await ShowOverlayAndLoad(sceneName); } private async UniTask ShowOverlayAndLoad(string sceneName) { ActivateOverlay(true); GameObject centerEyeAnchor = GameObject.Find(CENTER_EYE_ANCHOR); _overlay_LoadingText.gameObject.transform.position = centerEyeAnchor.transform.position + new Vector3(0f, 0f, 3f); await UniTask.Delay(MARGIN_MILLISECOND); await SceneManager.LoadSceneAsync(sceneName); ActivateOverlay(false); } private void ActivateOverlay(bool isActive) { _overlay_Background.enabled = isActive; _overlay_LoadingText.enabled = isActive; } }
実機確認
こんな感じです.
Tipsですが,Compositor LayerはそもそもLayerが違うため,
Unity Recorderで録画しても,Compositor Layerが表示されません.アプリケーションの画面のままになります.
(つまりUnity Recorderで録画した動画やUnity Editor の Game Scene で見ると,キンクリ発動しているように見える.)