HTC VIVE で頭の位置を固定する
どういうこと?
向きは固定しません。振り向くことはできます。
言い換えると、ヘッドトラックはしているのだけど、毎フレーム必ず、頭の座標を指定の位置に戻します。
なにがしたいの?
THETA などの全天球動画を、撮影位置からぴったり見たいです。
実際やってみてわかったこと
残念なことにこの処方は酔うことが確認できました。THETA を見るようなケースの場合は、動画像を Skybox に貼り付けるなど、十分に大きくしてしまえば実質同じなので、そのほうが良いと思います。
やりかた
前調査
こちらの記事様でやられていましたが、 2017年5月時点の SteamVR SDK を利用した場合、これだけでは不充分であることがわかりました。
SteamVR Plugin を利用する場合
SteamVR/Scenes
に入っている Example
シーンの頭の位置を固定してみます。今回は、原点正方向を基準位置とします。
この example
シーンのカメラでは、Main Camera (origin)
のなかに Main Camera (head)
さらにその中に Main Camera (eye)
という構造になっていますが、Play してみると、Main Camera (eye)
が (origin)
の直下に展開されます。
この (eye)
の座標を0に固定すれば良いと思うのですが、どこかのタイミングで強制的にヘッドトラッキングに基づく座標に上書きされてしまうので、ここでは 親のcameraの座標を相対的に戻るようずらす という解決策を取ります。
Play前
Play中
もう2工夫必要です。
example
シーンでは、Main Camera(head)
が Scale 0.1 倍にされています。そこで、Main Camera (origin)
を相対的にずらす際に、そのスケール分戻して上げる必要があります。- カメラがY軸中心で180度回っているので、これを戻して上げる必要があります。(これはEditor上で行います)
以上のことをやるためのスクリプトは以下のもので、これを Main Camera (origin)
に貼り付ければOKです。
// FixHMDPosition.cs using UnityEngine; using UnityEngine.VR; public class FixPosition : MonoBehaviour { void LateUpdate() { Vector3 trackingPos = InputTracking.GetLocalPosition(VRNode.CenterEye); transform.parent.position = -trackingPos * 0.1f; } }
カメラの方向をY軸180度戻したので、それに合わせてシーンも修正する必要があります。
弊害
このハックで頭の位置を固定すると、 一見 Unity で作った世界に対して頭部位置が固定されたように描画されますが、 HTC VIVE で言うところの「シャペロン境界」は、メインカメラとの位置関係で決められているらしく、頭を動かした結果シャペロン境界が表示されるエリアに入ると、表示されて、結果自分が動いていることが露見します。これは、世界の中に動いているものと動いていないものが混在する状態になるので、気持ち悪さの大きな原因になります。
そこで、今回の手法で頭部位置を 「Unityで作る世界の中で」固定したい場合は、別の方法でシャペロン境界を非表示にする必要があります。
こりんさんが過去にまとめてくださっていたのですが、今やってみると少し仕様が変わっていたようで、steamvr.vrsettings
にもう一行追加する必要があります。
{ "collisionBounds" : { "CollisionBoundsColorGammaA" : 0, "CollisionBoundsStyle": 4 // Developer モードにしないと非表示にはできない }, ... }
言うまでもないことですが、これに加えてまずはじめに、体験者がシャペロン境界に届かないよう、プレイエリアの中心に座ってもらって始めるのが前提になります。
SteamVR の [Camera Rig] を使わない場合
基本は SteamVR Plugin を利用する場合と同じなのですが、以下の手順となります。
- Player Settings で
Virtual Reality Supported
にチェックを入れ、OpenVR
対応とします Main Camera
を、空のゲームオブジェクトで一層包みます。仮にこれをCameraParent
とします。- その
Main Camera
に対し、先程と 「ほぼ」 同様のFixHMDPosition.cs
をアタッチしますが、先程のサンプルとは異なりCameraParent
はスケールが等倍のままなので、FixHMDPosition.cs
でスケールをかけた部分を消します。
// 前略 transform.parent.position = -trackingPos; // * 0.1f しない // 後略
さいごに
最初に書きましたが、 THETA の全天球動画を見る、という用途には向いていない気がしました。ヘッドトラッキングに慣れた体からはもう戻れないのね。。。。