Unity+Oculus Rift開発メモ

最終更新日:2018年11月18日

UnityでOculus Rift対応ソフトを開発する方法やTipsをまとめています。

更新履歴

(2018年11月18日)Oculus Integrationに簡単に対応
(2018年2月4日)いくらか記述を更新
(2017年11月29日)微修正
(2017年6月15日)「立って体験するソフトを作るには」にUnity本体のAPIを使う方法を追記、その他リンク等を修正
(2017年4月9日)Unityの対応バージョンについて更新


UnityでOculus Rift対応ソフトを作るには

UnityのOculus Rift対応の概要について

Unityは標準でRiftに対応しています。Virtual Reality SupportedでSDKにOculusを追加してWindows向けにビルドすると、Riftで動作するアプリを作成できます(詳細は「Unity標準のVR機能(UnityEngine.XR)メモ」を参照してください)。

さらに、コントローラの表示やアバター機能そのほかOculusプラットフォーム固有の機能を使用したい場合や、Oculusストアにリリースできるアプリを作る場合は、Asset StoreにあるOculus IntegrationをインポートしてMain CameraをOVRCameraRigプレハブに入れ替えます。

基本的にはOculus Integrationを使用するのがいいと思います。本ページはOculus Integrationを使用する場合について解説します。

Oculus Integrationは新しいバージョンのUnityに対応していないことがあります。Compatibility and Version RequirementsのページでサポートしているUnityのバージョンを確認できます。

Oculus Riftで自作アプリを実行できるようにする

自作のソフトをOculus Riftで動かすには、Oculusアプリの設定を変更する必要があります。Oculusアプリを起動して左のSettingsを選択、Generalタブを選択してUnknown Sourcesをオンにします。この設定は一度変更するとOculusアカウントに保存されます。

Oculus Riftアプリをビルドするには

Unityで新規プロジェクトを作成してください(2018-11-18現在、Unity 2018.2がおすすめです)。

Asset StoreのOculus Integrationをインポートします。新しいバージョンのOVRPluginが使用可能というダイアログが出てきた場合はYesをクリックし、Restartを押します。

試しにProjectウィンドウでOculus/VR/Scenes/Cubesシーンを開いてみてください。エディタで再生ボタンを押して、Riftをかぶって立方体がたくさん浮かんでいる様子が見られれば成功です。

さらにビルドすると、Riftで見られる実行ファイルを作ることができます。

動かないときは

以下のようなことを試してみてください。

  • Player SettingsのXR SettingsでVirtual Reality Supportedがチェックされていて、Virtual Reality SDKsに「Oculus」が追加されていることを確認する
  • Unityのバージョンを変えてみる
  • ビデオカードのドライバを最新版にする

エディタ上で再生するとカクつくんだけど

Game ビューの「Maximize on Play」をオンにして再生してみてください。最大化せずに再生すると、インスペクタの表示などが負荷になり、フレームレートが低下します。


開発Tips

Rift対応はどのように行われるの?

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

また、実行時にカメラのField of Viewがヘッドセットにあわせて変更され、PositionとRotationがヘッドセットの動きで上書きされるようになります。このため、スクリプトなどでカメラを動かす場合には、他のオブジェクトの子オブジェクトにする必要があります。

Riftが接続されていなければ通常通りPCに表示されます。これによって、Riftと非Oculus Rift両対応のソフトを作ることができます。

Oculus Integrationは何をするの?

Unity標準のVRサポート機能にはない、以下のようなOculusのヘッドセット固有の機能が含まれています。

  • Oculus標準カメラリグ(OVRCameraRig)
  • Xbox Oneコントローラー、Oculus Remote、Oculus Touchの抽象的な入力(OVRInput)
  • ヘッドセットの各種データを取得(OVRDisplay)
  • 位置トラッキングカメラの状態を取得(OVRTracker)
  • Gear VRの諸機能(Moonlightフォルダに格納されているアセットを使用)
  • 複数の位置トラッキングカメラのサポート
  • Oculus Storeへの配信

なお、Oculus Integrationを使用すると、Oculusのヘッドセットでしか動かないアプリケーションになります。他のヘッドセットへの移植を考慮する場合考慮が必要です。

位置トラッキングの合わせ方を教えて

まず、シーン内のカメラをプレイヤーの頭の位置(正確には両目の中間)に配置してください。その上で、実行中にちょうどいい場所に立つ、椅子に深く座るなどして、キーなどでトラッキングのリセットをかけます。以下のようなスクリプトを適当なゲームオブジェクトにアタッチして使います。

using UnityEngine;

public class Recenter : MonoBehaviour
{
    void Update()
    {
        // Rキーで位置トラッキングをリセットする
        if (Input.GetKeyDown(KeyCode.R))
        {
            OVRManager.display.RecenterPose();
        }
    }
}

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

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

立って体験するソフトを作るには

OVRCameraRigオブジェクトをシーンの地面と同じ高さに配置して、OVR ManagerコンポーネントのTracking Origin TypeをFloor Levelに変更します。

再生するとVR空間と現実世界の地面の高さが合っているはずですが、合っていない場合は、OculusソフトウェアのDevices > Configure Rift > Reset Sensor Trackingで位置トラッキングカメラのキャリブレーションをやり直します。途中で身長を正確に入力し、立った状態でキャリブレーションする必要があります。ボタンを押したときのヘッドセットの高さと入力した身長をもとに床の高さが決定されます。

描画設定やアンチエイリアスについて

アンチエイリアスについては、Oculus VR社のvrdaveb氏のコメントによれば、Player SettingsでRendering PathをForwardにして、Quality SettingsのAnti AliasingでMulti Sampling(MSAA)をかけることが推奨されています。Deferredは帯域コストが高く、MSAAが使えないため不利になります。MSAAはイメージエフェクトのアンチエイリアス(FXAA)よりも軽量で、ポリゴンのエッジが綺麗です。

同じくPlayer SettingsのColor SpaceをGammaではなくLinearにすることが推奨されています。

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

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

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

また、Oculus Integrationで、GPUの使用率が高くなるとフレーム落ちしないように自動的に解像度を下げるAdaptive Resolutionという機能を使用できます。OVRCameraRigのOVR ManagerにあるEnable Adaptive Resolutionをオンにしてください。

Oculus Riftの位置・向きを取得するには?

Oculus Riftの位置・向きによってカメラのtransformが変化しますので、これを参照できます。

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

using UnityEngine.XR;
...
Vector3 position = InputTracking.GetLocalPosition(XRNode.CenterEye);
Quaternion rotation = InputTracking.GetLocalRotation(XRNode.CenterEye);

XRNode.LeftEye/RightEye/CenterEye/Headでそれぞれ左右の目と、目の中間、頭部の位置・向きが取得できます。CenterEyeとHeadは同じ値が戻ってくるようです。

Oculus Riftの接続チェックをするには?

Riftが接続されていれば、XRDevice.isPresentがtrueになります。

using UnityEngine.XR;
...
if (XRDevice.isPresent) { /* Riftあり */ }

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

XRDevice.modelまたはXRSettings.loadedDeviceNameを使用します。

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

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

XRDevice.model = Oculus Rift CV1
XRSettings.loadedDeviceName = Oculus

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

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

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

using UnityEngine;

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

なお当然ですが、一画面余分にレンダリングしますので、そのぶん負荷が増えます。フレーム落ちする場合は、起動ダイアログの解像度設定を下げてみてください。

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

シーンにカメラを複数設置して同時描画できる?

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

サウンドに関する注意

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

Xbox Oneコントローラーの入力を取るには?

Unity本体で取得する場合、Input Managerにおけるジョイスティックの軸とボタンのマッピングは以下のようになっています。

Axis/Button
X axis 左アナログスティック X軸(左-1~右1)
Y axis 左アナログスティック Y軸(上-1~下1)
4th axis 右アナログスティック X軸(左-1~右1)
5th axis 右アナログスティック Y軸(上-1~下1)
7th axis 方向キー X軸(左-1、右1)
8th axis 方向キー Y軸(下-1、上1)※アナログスティックと逆
3rd / 9th axis LT(リリース-1~最大1)
6th / 10th axis RT(リリース-1~最大1)
joystick button 0 Aボタン
joystick button 1 Bボタン
joystick button 2 Xボタン
joystick button 3 Yボタン
joystick button 4 LB
joystick button 5 RB
joystick button 6 ビューボタン(中央左のボタン)
joystick button 7 メニューボタン(中央右のボタン)
joystick button 8 左アナログスティック押し込み
joystick button 9 右アナログスティック押し込み
joystick button 10 Xboxボタン

または、Oculus IntegrationのOVRInputで取得します。

Oculus Remoteの入力を取るには

ジョイスティック入力として取得できます。Input Managerにおける軸とボタンのマッピングは以下のようになっています。Xbox Oneコントローラーの方向キーおよびA/Bボタンと同一になっています。

Axis/Button
7th axis 方向キー X軸(左-1、右1)
8th axis 方向キー Y軸(下-1、上1)
joystick button 0 Selectボタン
joystick button 1 Backボタン

Oculusボタンと2つのボリュームボタンにはアクセスできません。

また、Input.GetJoystickNames()で取得できる文字列の配列に”Oculus Remote”が入っています。

Oculus IntegrationのOVRInputでも取得できます。

Oculus Homeに戻るには

Application.Quit()で終了します。または、Universal MenuでいつでもExit to Homeできます。終了時にOnApplicationQuit()が呼ばれますので、ここでデータの保存処理などを行います。

Oculus Riftをかぶっていないときも動作が止まらないようにするには

Oculus Riftには、ヘッドセット内の中央上に近接センサーがあり、ヘッドセットをかぶっていないときには表示が消えるようになっています。これは有機ELパネルの焼き付きを防ぐためで、現状ソフトウェアで無効にすることはできません。

展示等でアプリケーションが動作しつづけるようにしたい場合は、一つの方法として、この近接センサーにテープか何かを貼って騙すことができます。

また、もう一つの方法として、PC側の画面だけは動くようにすることができます。Player SettingsのRun In Backgroundをチェックして、Target Eyeを「None (Main Display)」にしたPC側カメラをシーンに配置します。PC側カメラのDepthはヘッドセット側カメラのDepthよりも小さくしてください。こちらの方法のほうが、待機中にデモ画面を流したりしやすいかもしれません。

なお、このままだとPC側カメラで常にレンダリングするためそのぶんの描画負荷がかかってしまいますが、Oculus UtilitiesのOVRCameraRigを使用して、OVRManagerコンポーネントのisUserPresentでヘッドセットをかぶっているかどうかを取得できますので、これを利用して、必要ないときにPC側カメラを無効にすることができます。

位置トラッキングカメラの位置を取得するには

OVRManager.tracker.countで接続している位置トラッキングカメラの数が、OVRManager.tracker.GetPose({番号})で各カメラのpositionとorientationが取得できます。


参考リンク

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