【Unity(C#)】ARFoundationのImageTrackingで複数の画像マーカーそれぞれに対応したARオブジェクトを出現させる


はじめに

ARFoundationのImageTrackingを使えば登録した画像マーカーを認識することにより、
任意のARオブジェクトを出現させることができます。

【参考リンク】:ARFoundationを触ってみた

実は画像マーカーを複数登録することも可能なようです。
今回はそれについて調べたのでメモします。

内容としては"認識した画像マーカーそれぞれの上にARオブジェクトを出す"だけに留めます。
下記のような難しい実装の手法をこの記事で書くわけでないということ予め明示しておきます。

×複数の画像マーカーの位置関係を固定した、広域対応のARの手法。
【参考リンク】:【GyroEye Holo】マーカー位置

デモ

画像マーカー二枚のそれぞれに対応したARオブジェクトを表示しています。
あまり用途は無いかもですが、ロスト時に非表示にする実装も試してみました。

バージョン情報

諸々名前 バージョン
Unity 2020.3.4f1
ARFoundation 4.0.12
ARCore XR Plugin 4.0.12
ARKit XR Plugin 4.0.12
XR Plugin Management 4.0.1

※Andoroidでしか試していませんが、ARFoundationなのでiosでも動くはず、、、です。

下準備

バージョン情報に従って一通りビルドできるまでの環境構築が終わったら、下記画像を参考にReferenceImageLibraryを作成します。

次にReferenceImageLibraryを設定します。

あとはAR Session OriginAR Tracked Image Managerを設定し、
Serialized Libraryに先ほど作成したReferenceImageLibraryを設定すれば準備完了です。

コード

適当なオブジェクトにアタッチ
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

/// <summary>
/// 画像マーカー複数対応のサンプル
/// </summary>
public class MultiMarker : MonoBehaviour
{
    /// <summary>
    /// マーカー用オブジェクトのプレハブ
    /// </summary>
    [SerializeField] private GameObject[] _arPrefabs;

    /// <summary>
    /// ARTrackedImageManager
    /// </summary>
    [SerializeField] private ARTrackedImageManager _imageManager;

    /// <summary>
    /// マーカー用オブジェクトのプレハブと文字列を紐づけた辞書
    /// </summary>
    private readonly Dictionary<string, GameObject> _markerNameAndPrefabDictionary = new Dictionary<string, GameObject>();

    private void Start()
    {
        _imageManager.trackedImagesChanged += OnTrackedImagesChanged;

        //辞書を作る 画像の名前とARオブジェクトのPrefabを紐づける
        for (var i = 0; i < _arPrefabs.Length; i++)
        {
            var arPrefab = Instantiate(_arPrefabs[i]);
            _markerNameAndPrefabDictionary.Add(_imageManager.referenceLibrary[i].name, arPrefab);
            arPrefab.SetActive(false);
        }
    }

    private void OnDisable()
    {
        _imageManager.trackedImagesChanged -= OnTrackedImagesChanged;
    }

    /// <summary>
    /// 認識した画像マーカーに応じて紐づいたARオブジェクトを表示
    /// </summary>
    /// <param name="trackedImage">認識した画像マーカー</param>
    private void ActivateARObject(ARTrackedImage trackedImage)
    {
        //認識した画像マーカーの名前を使って辞書から任意のオブジェクトを引っ張り出す
        var arObject = _markerNameAndPrefabDictionary[trackedImage.referenceImage.name];
        var imageMarkerTransform = trackedImage.transform;

        //位置合わせ
        var markerFrontRotation = imageMarkerTransform.rotation * Quaternion.Euler(90f, 0f, 0f);
        arObject.transform.SetPositionAndRotation(imageMarkerTransform.transform.position, markerFrontRotation);
        arObject.transform.SetParent(imageMarkerTransform);

        //トラッキングの状態に応じてARオブジェクトの表示を切り替え
        arObject.SetActive(trackedImage.trackingState == TrackingState.Tracking);
    }

    /// <summary>
    /// TrackedImagesChanged時の処理
    /// </summary>
    /// <param name="eventArgs">検出イベントに関する引数</param>
    private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.added)
        {
            ActivateARObject(trackedImage);
        }

        foreach (var trackedImage in eventArgs.updated)
        {
            ActivateARObject(trackedImage);
        }
    }
}

やっていることは下記です。

①"ReferenceImageLibraryに登録した画像の名前"と"表示したいARオブジェクトのPrefab"を辞書で紐づける。
②画像マーカー認識時に画像の名前に紐づけられたARオブジェクトを出現させる。


ARTrackedImageManager.trackedImagesChanged

trackedImagesChangedに画像マーカー認識時のイベントハンドラーを設定できます。

イベントハンドラーの引数であるARTrackedImagesChangedEventArgsからは
下記3種類の情報をそれぞれのタイミングで得ることができます。

Type Name Description
List added The list of ARTrackedImages added since the last event.
List updated The list of ARTrackedImages updated since the last event.
List removed The list of ARTrackedImages removed since the last event.

【引用元】:Struct ARTrackedImagesChangedEventArgs

addedupdatedの使い道は理解できたのですが、removedがいまいち使い道がわかりませんでした。
The list of ARTrackedImages removed since the last event.という説明から、画像をロストした時?かと思い、
その前提で実装を組んでみましたが、何をやっても呼ばれずです。

使い道等知ってる方いたら教えてください。


TrackingState

先ほど画像をロストした時について実装を試みたと書きましたが、
それについてはTrackingStateが有効でした。

ARTrackedImage(認識した画像)から三種類取得できます。

TrackingState Description
Limited Some tracking information is available, but it is limited or of poor quality.
None Not tracking.
Tracking Tracking is working normally.

【引用元】: Enum TrackingState

簡単に言うとLimitedは認識精度が低い時、Noneは非認識時、Trackingは認識した時 といった感じです。

おわりに

画像マーカーを動かすと、再認識とロストを繰り返してARオブジェクトがカクカクし始めるので、
平面に固定することを前提として使った方がいいかも と色々と実験しながら思いました。

参考リンク

AR Foundation Improved Image Tracking - Multiple Objects/Images - Unity Augmented Reality/AR