【ARKit×SteamVR】AR世界とVR世界を共有する方法

 はじめまして、インターン生の河野です!この記事は、 私のインターン活動の主題である「SteamVR Knuckles EV3 コントローラを試す」において行った、「iPhone の ARKit アプリ」と「PC の SteamVR アプリ」の世界の基準点を共有する方法について書いたものです。 主題である私のインターン活動の詳細は以下のサイトを見てください。

【SteamVR Knuckles EV3】手に”着ける”コントローラを試す!- Techno sports

 この記事で紹介する基準点の共有方法は、平島さんの「【Hi5 VR Glove】指の動きを追ってみる! – Techno sports」と同様のものです。それを参考にしつつ、今回は回転を一般化し、また ARKit の画像認識系についての記事を追加した形になります。

1. AR世界とVR世界の方法共有について

 複数人でAR世界を共有するには、アプリの“基準点”を合わせる必要があります。 その方法として、共通画像を用いた画像トラッキングが多く使用されています。カメラを用いて画像を認識し、その対象画像のカメラ画像における姿勢を求めることで、同時に対象画像に対するカメラ位置を特定できます。これを各アプリで行うことで、認識画像を基準とした位置共有ができるわけです。

 問題は、“AR”世界だけでなく“VR”世界も共有する必要がある場合です。VR世界に現実世界を見れるカメラを追加することで同様に位置を共有することもできますが、VR世界が1つだけなら VIVE トラッカーをVR空間の基準点とし、ARアプリの認識画像の上に直に置くことで基準点を同じにする方法なら、簡単に基準点を合わせられます。

 ちなみに、基準点を同じにすれば、アプリ側はサーバから受け取る位置・姿勢情報を加工することなくそのまま使用できるので、考えることが減ります。

 VR世界が多数になった場合は、各VR世界に外部カメラを導入する方法や、同じVIVEトラッカーを複数PCにペアリングできないので、各VR世界が持つ別のトラッカーを置く位置を規定値ずらすことで位置を合わせるなどが考えられます。

2. ARKit で画像をトラッキングし、その画像の位置・姿勢をAR世界の原点にする方法

 今回は、ARKit のARReferenceImage を用いました。これに関しては、「Unityで始めるARKit入門 画像トラッキング編 – おもちゃラボ 」が詳しいです。

 上記サイトの指示に従って、 ARKit のARカメラを司る ARCameraManager“UnityARCameraManager.cs” に認識対象画像のある ARReferenceImageSet をアタッチすると、勝手に画像をトラッキングしだします。設定する画像は「1024×1024」以下で、PNG画像よりもJPEG画像が良いです。エラーが出るか、トラッキングできない場合があります。

 画像をトラッキングしたときに名前空間 UnityEngine.XR.iOS にある以下のイベントが発火します。プログラムは ARKit にある “GenerateImageAnchor.cs”を参考にするとよいです。

UnityARSessionNativeInterface.ARImageAnchorAddedEvent初めて ARReferenceImageSet の中にある画像を認識したとき
UnityARSessionNativeInterface.ARImageAnchorUpdatedEvent画像を再認識したとき
UnityARSessionNativeInterface.ARImageAnchorRemovedEvent画像を認識できなくなったとき

 今回は「アプリ原点をトラッキングした画像の位置・姿勢にする」メソッドを Add と Update イベントに登録しました。

 アプリ原点を変えるには、UnityARSessionNativeInterface.GetARSessionNativeInterface().SetWorldOrigin(Transform) メソッドを用います。ただ、認識画像の位置・姿勢を持つ ARImageAnchor.transform「Matrix4×4型」なので、それから取れる位置(Vector3)・姿勢(Quaternion)から Transform に直す必要があります。

using UnityEngine.XR.iOS;
public Transform tr; // どこかから持ってくるか、自分でInstantiateするか:Transform型は即時生成・代入不可なので
public ARReferenceImage referenceImage; // インスタンス上でアタッチするか、基準点にしたい画像の名前
void UpdateImageAnchor(ARImageAnchor arImageAnchor){
    if (arImageAnchor.referenceImageName == referenceImage.imageName){ // ImageSetの画像群のうち、今認識したものが目的の基準点画像か判定
        Vector3 position = UnityARMatrixOps.GetPosition(arImageAnchor.transform);
        Quaternion rotation = UnityARMatrixOps.GetRotation(arImageAnchor.transform);
        tr.position = position;
        tr.rotation = rotation;
        UnityARSessionNativeInterface.GetARSessionNativeInterface().SetWorldOrigin(tr); // 原点を、指定したTransformにする
    }
}

3. VIVE トラッカーの位置をVR世界の基準点にする方法

 SteamVR コントローラは、VR空間の座標系になっています。原点は HMD 基準ではありますが、立つ・座るなどのモードによって HMD の座標が変わっているため、零点でない HMD は原点になりません。今回は各コントローラを VIVE トラッカーから見た相対座標とすることで、VIVE トラッカーを零点とする位置情報をARアプリに送信します。

 そこで、トラッカーオブジェクトの子にコントローラオブジェクトをおくことでトラッカーに対するコントローラの相対座標を取る方法が思いつきますが、SteamVR の位置更新は相対座標欄に位置を更新するためか、位置がおかしくなります。そのため、自前で相対座標を計算する必要があります。

 方法は、各コントローラの位置・姿勢をトラッカーの位置・姿勢で引いてやればよいだけです。姿勢差分は、クォータニオンなので Inverse を後ろから掛けることと等価ですが、以下のように基準方向へのトラッカー姿勢差分を計算した方が位置回転も統一的に行えます。また、コントローラの速度も回転させないと投げるなどのアクションを行うときにおかしくなります。 疑似コードで書けば以下のようになります。

Quaternion (VR世界原点からトラッカーの前方方向への姿勢差分) =  Quaternion.FromToRotation((トラッカー).up, Vector3.forward);
Vector3 (トラッカーからコントローラへの位置ベクトル) = (コントローラのワールド位置) -(トラッカーのワールド位置);

(トラッカーに対するコントローラの相対位置) = (VR世界原点からトラッカーの前方方向への姿勢差分) * (トラッカーからコントローラへのベクトル);
(トラッカーに対するコントローラの相対姿勢) = (VR世界原点からトラッカーの前方方向への姿勢差分) * (コントローラのワールド姿勢);
(トラッカーに対するコントローラの相対速度ベクトル) = (VR世界原点からトラッカーの前方方向への姿勢差分) * (コントローラの速度ベクトル);

4. 最後に

 今回は、ARKit を用いたAR世界と、SteamVR を用いたVR世界の共有方法を紹介しました。この方法は空間共有方法の一例です。AR側の静的な認識画像に頼っている点、VR側の VIVE トラッカーのトラッキング性能が少々の欠点になっていますが、簡便な方法です。VR・AR・MRと段々と発展している今、空間共有は重要であり、これからもこのような簡便かつ精度良い方法を追っていく必要があります。

 ちなみに、今回の ARKit と SteamVR で空間共有方法の利点でまた別にあるのが、Unity で共通した開発ができることがあったりします。

 今回紹介した方法以外で何か良い方法、間違っていることなどがあったら、コメントをいただけると幸いです。

コメントを残す