はじめに

今回は Meta Quest の v71 から追加された グループベースの共有空間アンカー についての記事です。
Unity での実装方法として Meta XR SDK と AR Foundation + OpenXR: Meta の2つの選択肢がありますが、今回の記事では Meta XR SDK を使用します。

開発環境

  • Unity 6000.1.8f1
  • OpenXR Plugin 1.14.3
  • Meta XR SDK 77.0.0
    • Core
    • Interaction
    • Platform

概念

  • ユーザーは各々が作成した空間アンカーをMetaのクラウド経由で他ユーザーに共有することができます。
  • 共有の際にGUIDを指定し、そのGUIDを知っているユーザーのみが共有された空間アンカーにアクセスできます。
  • 共有用のGUIDは何らかの方法で他ユーザーに共有する必要があります。

空間アンカーを作成する

1. 空間アンカーを有効化する

  • OVR Manager の Quest Features の Anchor Support を Enabled にします。

2. GameObject を空間アンカーにする

  • GameObject に OVRSpatialAnchor コンポーネントを追加することで空間アンカーにすることができます。
  • OVRSpatialAnchor コンポーネントを追加した直後はまだアンカーの作成が完了しておらず、 OVRSpatialAnchor.Created が True になるまで待機する必要があります。
public async UniTask<OVRSpatialAnchor> CreateAnchorAsync(GameObject anchorObject)
{
    if (!anchorObject.TryGetComponent(out OVRSpatialAnchor anchor)) anchor = anchorObject.AddComponent<OVRSpatialAnchor>();
    await UniTask.WaitUntil(() => anchor.Created);
    return anchor;
}

3. 空間アンカーを永続化する

  • 作成した空間アンカーを Meta Quest のローカルに保存します。
  • 読み込みや削除の際に OVRSpatialAnchor.Uuid が必要になるので PlayerPrefs などで別途保存しておく必要があります。
public async UniTask SaveAnchorAsync(OVRSpatialAnchor anchor)
{
    var result = await anchor.SaveAnchorAsync();
    
    if (!result.Success) throw new Exception($"Save failed with error {result.Status}.");
    
    // anchor.Uuid を PlayerPrefs などに保存する
}

4. 永続化した空間アンカーで位置合わせをする

  • PlayerPrefs などに保存してある OVRSpatialAnchor.Uuid で永続化した空間アンカーを読み込み位置合わせをします。
public async UniTask LocalizeAnchorsAsync(IEnumerable<Guid> uuids, Action<bool, UnboundAnchor> onLocalized)
{
    // PlayerPrefs などから読み込んだUUIDで空間アンカーを読み込む
    var unboundAnchors = new List<UnboundAnchor>();
    var result = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors);

    if (!result.Success) throw new Exception($"Load failed with error {result.Status}.");

    // 読み込んだ空間アンカーで位置合わせを行う
    foreach (var unboundAnchor in result.Value)
    {
        unboundAnchor.LocalizeAsync(LOCALIZE_TIMEOUT_SECONDS)
        .ContinueWith(onLocalized, unboundAnchor);
    }
}

アンカー共有用GUIDを共有する

  • ホストとなるユーザーが GUID を作成し、他ユーザーに共有します。
  • 共有する方法に縛りはなく Photon などの外部ライブラリでの共有も可能です。
  • 本記事では Meta XR SDK に含まれる Colocation Discovery 機能を使用します。

1. Colocation Discovery を有効化

  • OVR Manager の Quest Features の Colocation Session Support を Required にします。

2. 【ホスト】コロケーションの宣伝を開始する

  • ホストがバイナリに変換した GUID をコロケーションで宣伝します。
private async UniTask<bool> startAdvertiseAsync(ColocationSessionData data)
{
    var json = JsonUtility.ToJson(data);
    var bytes =  Encoding.UTF8.GetBytes(json);
    var result = await OVRColocationSession.StartAdvertisementAsync(bytes);
    return result.Success;
}

3. 【クライアント】コロケーションの発見を開始する

  • クライアントはコロケーションの発見を開始し、ホストから宣伝されている GUID を受け取ります。
public async UniTask StartDiscover(Action<OVRColocationSession.Data> onDiscovered)
{
    OVRColocationSession.ColocationSessionDiscovered += onDiscovered;
    var result = await OVRColocationSession.StartDiscoveryAsync();
    if(!result.Success) throw new Exception($"Failed to start discovery: {result.Status}");
}

空間アンカーを共有する

1. 空間アンカーを有効化する

  • OVR Manager の Quest Features の Shared Spatial Anchor Support を Required にします。
    • (補足) Shared Spatial Anchor Support を Required にすると Anchor Support も強制的に Enabled になります。

2. 【ホスト】空間アンカーを共有する

  • 空間アンカーを GUID を指定して共有します。
public async UniTask ShareAnchorsAsync(IEnumerable<OVRSpatialAnchor> anchors, Guid guid)
{
    var result = await OVRSpatialAnchor.ShareAsync(anchors, guid);
    if(!result.Success) throw new Exception($"Failed to share anchors: {result.Status}");
}

3. 【クライアント】共有された空間アンカーで位置合わせをする

  • GUID に紐付いて共有された空間アンカーをクラウドから取得し、位置合わせを行います。
  • 位置合わせが完了すると、空間アンカーの位置や向きなどの情報を持った UnboundAnchor 構造体が取得できます。
public async UniTask LocalizeSharedAnchors(Guid uuid, Action<bool, UnboundAnchor> onLocalized)
{
    var unboundAnchors = new List<UnboundAnchor>();
    var result = await OVRSpatialAnchor.LoadUnboundSharedAnchorsAsync(uuid, unboundAnchors);
    if(!result.Success) throw new Exception($"Load failed with error {result.Status}.");
    foreach (var unboundAnchor in result.Value)
    {
        unboundAnchor.LocalizeAsync(LOCALIZE_TIMEOUT_SECONDS).ContinueWith(onLocalized, unboundAnchor);
    }
}

4. 【クライアント】位置合わせ後のデータを元に空間アンカーを作成する

  • UnboundAnchor 構造体のままではアンカーとして使用できないので、アンカーとする GameObject に位置と向きを適用して紐付けます。
private void onLocalizedAnchor(bool isSuccess, UnboundAnchor unboundAnchor)
{
    if (!isSuccess) return;
    if (!unboundAnchor.TryGetPose(out var pose)) return;
    var anchorObject = Instantiate(_anchorPrefab);
    anchorObject.name = $"Anchor_{unboundAnchor.Uuid}";
    anchorObject.transform.SetPositionAndRotation(pose.position, pose.rotation);
    var anchor = anchorObject.AddComponent<OVRSpatialAnchor>();
    unboundAnchor.BindTo(anchor);
}

おわりに

GUID の共有だけで簡単に空間アンカーの共有が実装できました。
空間アンカーの共有ができれば、その配下のオブジェクトの Transform も一致させることができます。
スペースの作成なども不要なので、見るだけのシンプルなコンテンツであれば、手軽に体験できると思います。

ただ、機能が不足している点も目についたので、次回は AR Foundation + OpenXR: Meta についてと、不足している機能について書いていこうと思います。
ぜひ次回もご覧ください。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる