Unity標準のVR機能(UnityEngine.XR)メモ

最終更新日:2018年05月12日

Unity標準のVRサポート機能についてまとめる予定の場所です。書きかけです。

更新履歴

(2018年5月12日)renderViewportScaleがForwardレンダリングでしか使用できないのを付記
(2018年2月4日)「ボタン・トリガーの状態を取得するには」を公式ドキュメントへのリンクに変更、「位置トラッキングを無効化するには」を追加
(2018年2月3日)とりあえずページ作成


UnityのVRサポート機能について

UnityでVRアプリケーションを開発する場合、通常はSteamVRプラグインやOculus Utilities、Google VR SDK等各ヘッドセット向けのSDKを使用しますが、基本的なVRヘッドセット対応だけであればUnity本体のAPIのみで可能です。VR/AR/MRを包含するということで、UnityEngine.XRという名前空間になっています。

Unityで新規プロジェクトを作成し、Edit > Project Settings > PlayerでPlayer Settingsを開き、Virtual Reality Supportedをチェックしてください。「Virtual Reality SDKs」が表示されるので、必要なSDKを指定します。

SDKは上から順番に使用可能なものが使用されるようになっています。例えば「Oculus」はOculusアプリケーションを、「OpenVR」はSteam VRを使用します。Steam VRで優先的に実行されるようにしたい場合は「OpenVR」が「Oculus」より上になるように並べ替えます。

ビルドして実行するとヘッドセットで再生されます。また、PC用ヘッドセットの場合はエディタの再生ボタンでVRモードになります。

Virtual Reality Supportedをオンにすると何が起きるの?

ビルドすると必要なプラグインが出力ファイルに同梱され、ヘッドセットが接続されていると自動的にヘッドセットに表示するようになります。Render Textureの設定されていないカメラが自動的にステレオレンダリングになり、左右両画面のカリングやシャドウマップの共通化などの最適化が行われます。

また、実行時にカメラのPositionとRotationがトラッキングの動きで上書きされるようになります。たとえばカメラをスクリプト等で移動しているような場合、スクリプトでのTransformの更新が上書きされて無効化されてしまいます。対策としては、カメラを別のゲームオブジェクトの子オブジェクトにして親のほうをスクリプトで動かしてください。

カメラのField of Viewもヘッドセットにあわせて自動的に変更されます。

位置トラッキングについて

XRDevice.SetTrackingSpaceTypeでルームスケールもしくはステーショナリーのいずれかのトラッキングモードを設定できます。VR空間と現実世界について、床を基準に位置合わせするか、頭を基準に位置合わせをするかの違いです。

ヘッドセットによってデフォルトのモードが異なります(Riftではステーショナリー、ViveとWindows Mixed Realityヘッドセットではルームスケールなど)。複数のヘッドセットに対応する場合、明示的にモードを設定したほうがいいでしょう。

RoomScale

部屋を歩きまわることを想定したモードです。Unityのカメラを配置した場所が、トラッキング空間の中央地面になります。通常、カメラを(0, 0, 0)等に配置してその周辺にシーンを構築することになると思います。

using UnityEngine;
using UnityEngine.XR;

public class Recenter : MonoBehaviour
{
    void Start()
    {
        XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale);
    }
}

Stationary

椅子に座って体験することを想定したモードです。Unityのカメラを配置した場所が、プレイヤーの頭の位置になります。InputTracking.Recenterで位置合わせができます。

using UnityEngine;
using UnityEngine.XR;

public class Recenter : MonoBehaviour
{
    void Start()
    {
        XRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);
    }

    void Update()
    {
        // Rキーで位置トラッキングをリセットする(Stationaryでしか動作しない)
        if (Input.GetKeyDown(KeyCode.R))
        {
            InputTracking.Recenter();
        }
    }
}

スケール(縮尺)について

スケールの基本

ヘッドセットの映像はUnityのシーン内で左右の目の間隔ぶんずらした位置からレンダリングされ、この両眼視差によってVR空間の物体が現実的な大きさに感じられるようになっています。Unityは1ユニット=1メートルになっていますので、特に実写系のコンテンツではこれに従うのが基本です。例えば、身長170cmのキャラクターを出すときは、Unity上で頭頂まで縦1.7になるようにサイズを調整します。

オブジェクトのサイズを調整する際には、エディタ上でCubeオブジェクトを作って適当なスケールを入力して、定規代わりにすると便利です。

また、普段の生活で大きさをよく知っているものをVR空間に出してスケール感の手がかりにするのも良いかと思います。

体験者のスケールを変更したい

既存のプロジェクトのシーンが1ユニット=1メートルで作られていない場合や、巨人・小人の視界などを表現したいというような場合があります。カメラを別のオブジェクトの子にして、親オブジェクトのScaleを変更すれば実現できます。カメラ自身のScaleを変更しても反応しないので注意してください。

モーションコントローラーの使用

モーションコントローラーの位置・向きを取得するには

(Unity 2017.2以降)ゲームオブジェクトにTrackedPoseDriverコンポーネントをアタッチすると、ヘッドセットやコントローラーの動きに追従してTransformが変化します。

Unity 2018.1b5現在、トラッキングできるのはLeftEye、RightEye、Center、Head、LeftPose、RightPose、ColorCameraです。例えばHTC ViveのViveトラッカーには対応していません。対応する場合はSteamVRプラグインが必要です。

UseRelativeTransformオプションで、実行開始時のTransformを基準にするか、ゼロを基準にするかを変えられます。

もしくは、InputTracking.GetLocalPosition/GetLocalRotationで、カメラオブジェクトを基点にしたローカル座標と向きが取得できます。サンプルコードです。

using UnityEngine;
using UnityEngine.XR;

public class ViveControllerTest : MonoBehaviour
{
    void Update()
    {
        transform.localPosition = InputTracking.GetLocalPosition(XRNode.LeftHand);
        transform.localRotation = InputTracking.GetLocalRotation(XRNode.LeftHand);
    }
}

モーションコントローラーを振動させるには

残念ながら、Unity 2018.1b5現在、Unity本体のVRサポート機能ではモーションコントローラーの振動機能を利用できないようです。

ボタン・トリガーの状態を取得するには

ボタンやトリガーの状態をジョイスティック入力として取得できます。使用されているSDKおよびコントローラーによってマッピングが異なります。Unityのドキュメントにマッピングの一覧があります。

SDKがOpenVRの場合、コントローラーが接続されているとInput.GetJoystickNames()のジョイスティック名で「OpenVR Controller - Left」「OpenVR Controller - Right」がそれぞれ返ってきます。非接続状態になると空文字になります。

Viveコントローラーの3th axisのトリガーは、クリック直前まで引くと0.83前後まで上がり、カチッとクリックすると1にジャンプします。コントローラーの個体によっては完全には1にならなかったり、離しても0に戻らなかいことがあるようですので、判定にいくらか遊びを入れておいたほうが安全かもしれません。

SDKがOculusの場合、Oculus Touchが接続されていると「Oculus Touch - Left」「Oculus Touch - Right」が、Oculus Remoteが接続されていると「Oculus Remote」が返ってきます。

SDKがWindows Mixed Realityの場合、コントローラーが接続されていると「Spatial Controller - Left」「Spatial Controller - Right」がそれぞれ返ってきます。

UIについて

Unity GUIを使うには

Canvasを作成しRender ModeをWorld Spaceにして、Scaleを0.01などに設定して小さくしてシーンに配置します。

UIとのインタラクションについては、Unity公式のVR Samplesに含まれているスクリプトを使用するのが簡単です。使い方や、VRでのUIの考え方については、公式チュートリアルの「VRでのインタラクション」および「VRでのユーザーインターフェース」で解説されています。

描画設定について

カラースペース

Player SettingsのColor SpaceをLinearにするのが望ましいです。シェーディングが正確になるだけでなく、樽型変形のエイリアシングが大幅に減ります。

アンチエイリアスについて

MSAAを使用する場合が多いです。ポストプロセスのアンチエイリアスよりも軽量で、ポリゴンのエッジが綺麗に出るため、現状まだパネル解像度の低いVRヘッドセットに向いています。ただし、MSAAはDeferredレンダリングでは使用できないため、Forwardレンダリングにする必要があります。

Quality SettingsでMSAAの設定を4xあたりにします。負荷次第で8xに上げてもいいかもしれません。

レンダリング解像度を変更するには

GPUの描画負荷は、描画するピクセル数と、ピクセルあたりのシェーダの複雑さに比例することが多いです。GPUがボトルネックになってフレームレートが出ない場合、レンダリング解像度を下げると効果がある場合があります。

using UnityEngine.XR;
...
XRSettings.renderViewportScale = 0.5f; // デフォルトは1.0f

(Unity 2017.3以降)renderViewportScaleはForwardレンダリングでしか使用できないので注意してください。

また、XRSettings.eyeTextureResolutionScaleというAPIがあります。renderViewportScaleと異なり、描画用のテクスチャ自体を作り直します。1.0fよりも高く設定して描画解像度を上げることができます。こちらはDeferredレンダリングでも使用できます。

サウンドについて

ヘッドホンとスピーカーの定位の違い

ヘッドホンとスピーカーでは音声の定位が異なります。ヘッドホンは頭を動かすと一緒に回りますが、スピーカーは回りません。スピーカーで音声を出力する場合は、カメラからAudio Listenerを削除して別のオブジェクトにアタッチする必要があります。

その他の諸機能

接続しているデバイスの種類を知るには

XRDevice.modelで識別できます。

using UnityEngine.XR;
...
Debug.Log("XRDevice.model = " + XRDevice.model);

このような出力が得られます。

XRDevice.model = Oculus Rift CV1

PCの画面に別視点の映像を表示するには

カメラをシーンに追加し、Target Eyeを「None (Main Display)」に設定してください。また、どちらかのカメラから余分なAudio Listenerを削除してください。ビルドして実行すると別視点の画面がPC側に表示されます。

PC側のカメラのDepthの数値をRift側のカメラより大きくしておく必要があります。もしくは、XRSettings.showDeviceViewでRift側のカメラのミラー表示を無効にしてください(こちらのほうがより軽量なのではと思います)。

using UnityEngine;
using UnityEngine.XR;

public class HideVRView : MonoBehaviour
{
    void Start()
    {
        XRSettings.showDeviceView = false;
    }
}

なお当然ですが、一画面余分にレンダリングしますので、そのぶん負荷が増えます。

別のPCでプログラムをもう1つ動かして、ネットゲームの要領で同期表示することを検討してもいいかもしれません。

位置トラッキングを無効化するには

InputTracking.disablePositionalTrackingというAPIがあります。

TODO: 実際に各ヘッドセットで効力があるかチェック

シーンにカメラを複数設置して同時描画するには

通常のプロジェクトと同じようにカメラを複数使用してレイヤー表示できます。後から描画する方のカメラのClear FlagsをDepth OnlyもしくはDon’t Clearに、Depthを1以上に変更してください。また、余分なAudio Listenerを削除してください。

エディタのTips

Sceneビューのカメラアイコンが大きくて位置がよく分からない

SceneビューのバーにあるGizmosをクリックして、3D Gizmosのスライダーを左に動かしてみてください。


参考リンク

書いた人:こりん(@korinVR
VR開発メモトップ