はじめに
AR Foundationでのマーカー画像認識 (ImageTracking)の方法についてです🐼
※2020/08/08に修正と追記をしました。
サンプルプロジェクトはこちら↓
github.com
目次
- はじめに
- 目次
- ARFoundationの導入
- マーカーを使用する
- 複数マーカーの使用とマーカーごとにオブジェクトを変更する
- ※2020/08/08追記 Androidでうまく切り替わらなかった話
- 参考
ARFoundationの導入
AR Foundationでマーカー認識をする方法🐼
AR Foundationの最初の導入方法はこちらをご覧ください!
www.wwwmaplesyrup-cs6.work
ここまでできたら、以下マーカーの使用方法です。
マーカーを使用する
ヒエラルキーでAR Session Originを選択し、インスペクタでAR Tracked Image Managerコンポーネントを付けます。
続いて、Assets下にフォルダを一つ作ります。(名前はなんでも大丈夫です、今回はImageAssetsLibraryとします。)
そのフォルダの中で、右クリック>Create>XR>ReferenceImageLibraryを選択。
同じフォルダ内に、マーカーにしたい画像を入れます。
マーカー画像を入れたら、ReferenceImageLibraryを選択して、インスペクタのAdd Imageからマーカーにしたい画像をセットします。
そしてSpecify Sizeにチェックを入れて、Physical Sizeを入力します。メートルなので、0.1x0.1にしました。マーカーのサイズに合わせて入力してください。
マーカーを2つや3つ登録したいときはさらにAddImageをクリックすることでマーカーを複数登録することができます。
AR Session Originに付けたAR Tracked Image ManagerコンポーネントのReference Libraryに先ほどのReferenceImageLibraryをセットし、
その下のMax Number Of Moving Imagesで一度に認識したいマーカーの数を入れます。今回は1にします。
Tracked Image Prefabのところに、マーカー認識した時に出したいオブジェクトをセットします。
これで書き出ししてみると...
出ました!(マーカーちっちゃくて見えてないんですが後ろにあります)
複数マーカーを登録してみたところ、これもちゃんと出せました。
複数マーカーの使用とマーカーごとにオブジェクトを変更する
Tracked Image Prefabのところにオブジェクトをセットすると、Reference Image Libraryに登録したマーカー全部に同じオブジェクトが出てしまいます。
オブジェクトごとにマーカーを出したい時もありますよね。
Tracked Image Prefabにセットしたオブジェクトを消しましょう。
ReferenceImageLibraryから使用したいマーカーを複数セットし、それぞれの名前を順番に0、1、2....としてください。
この時に、Specify Sizeの変更も忘れずに。(ここを設定し忘れているとビルド時にエラーになります。)
※サイズはメートルなので、1.0で1メートルです。 実際に使用するマーカーのサイズにあわせて入力します。
そして、プロジェクトウィンドウで右クリックからCreate > C# Scriptで ImageRecognition クラスを作ります。
以下のスクリプトを貼り付けてください。
※こちらのスクリプト不具合があったので修正しました。以下のものは修正済みのコードです、後に説明を書き加えているので気になる方はみてください。
using UnityEngine.XR.ARFoundation; //これを忘れず付け足す public class ImageRecognition : MonoBehaviour { [SerializeField] private MarkerModelSwitcher _markerModelSwitcher; [SerializeField] private ARTrackedImageManager _arTrackedImageManager; private int _presentMarkerNum = -1; private void Awake() { _arTrackedImageManager.trackedImagesChanged += OnImageChanged; } public void OnImageChanged (ARTrackedImagesChangedEventArgs args) { string _name; int _nameNum; foreach (var a in args.updated) { _name = a.referenceImage.name; _nameNum = int.Parse (_name); Vector3 markerPos = a.transform.position; Quaternion qua = a.transform.rotation; if(_markerModelSwitcher.ArObj != null) { _markerModelSwitcher.ArObj.transform.position = markerPos; _markerModelSwitcher.ArObj.transform.rotation = qua; } if (a.trackingState == UnityEngine.XR.ARSubsystems.TrackingState.Tracking) { if (_nameNum == _presentMarkerNum) return; // Debug.Log ("MarkerName:::::::::::::::::::::::::::::::::" + _name); _markerModelSwitcher.SwitchingObject(_nameNum, markerPos, qua); _presentMarkerNum = _nameNum; } } } }
次に、MarkerModelSwitherクラスを作って、以下を貼り付けます。
public class MarkerModelSwither : MonoBehaviour { [SerializeField] private ARObjectManager _arObjManager; private int _pageNum = 0; private GameObject _arObj; public void SwitchingObject(int pageNum, Vector3 pos, Quaternion rot) { if (_arObj != null) _arObj.SetActive(false); this._pageNum = pageNum; _arObj = _arObjManager.GetArObjByNum(_pageNum); _arObj.SetActive(true); _arObj.transform.position = pos; _arObj.transform.rotation = rot; } }
次にARObjectManagerクラスを作って、以下を貼り付けます。
public class ARObjectManager : MonoBehaviour { [SerializeField] private GameObject[] _arObjs; private int _ObjNum = 0; private void Awake() { foreach (GameObject g in _arObjs) { g.SetActive(false); } } public GameObject GetArObjByNum(int _pageNum) { return _arObjs[_pageNum]; } }
3つできたら、Unityに戻ります。
ヒエラルキーのCreateから、Create Emptyで空のゲームオブジェクトを三つ作り、先ほどのスクリプトをそれぞれにセットします。
それぞれのインスペクタで、
①ImageRecognitionにはMarkerModelSwitherとAR Session Originをドラッグ&ドロップでセット
②MarkerModelSwitherにはObjManagerをドラッグ&ドロップでセット
③ObjManagerにはマーカーごとに出したいオブジェクトをセットします。最初にReferenceImageLibraryでマーカーの名前を数字にしました。
上記のスクリプトでその数字とElementの番号を対応させてあります。0番のマーカーを読み込んだら、Element0のオブジェクトを出す、といった具合です。
なので、Element0には0のマーカーにつけたいオブジェクトを、Element1には1のマーカーで出したいオブジェクトを...という感じでマーカーをセットしてください。
これで書き出してみましょう!
ちなみに、この後書き出したとき筆者は「これで出るはずなのにオブジェクトが出ない!!!」と数分迷った挙句、オブジェクトがでかすぎて自分が中に入り込んでて気づかなかった、というコントを一人でしていました。
1にしていてでかすぎたので、ここではオブジェクトのサイズを0.1に設定しています。皆さんも気をつけてください。
こんな感じになるよ🐼🐼🐼マーカーごとに違うオブジェクト出せた!
※2020/08/08追記 Androidでうまく切り替わらなかった話
ImageRecognition.cs
の中身を修正しました。
せっかくなのでこの部分のスクリプトの解説だけ書いておきます!
修正後のスクリプトはAndroidとiOS両方ちゃんと動きます🐼
//修正前-------------------------- if (_checkImageChangeNum != _nameNum) { Vector3 pos = a.transform.position; Quaternion rot = a.transform.rotation; _markerModelSwitcher.SwitchingObject(_nameNum, pos, rot); _checkImageChangeNum = _nameNum; } //修正後-------------------------- if (a.trackingState == UnityEngine.XR.ARSubsystems.TrackingState.Tracking) { if (_nameNum == _presentMarkerNum) return; // Debug.Log ("MarkerName:::::::::::::::::::::::::::::::::" + _name); _markerModelSwitcher.SwitchingObject(_nameNum, markerPos, qua); _presentMarkerNum = _nameNum; }
修正前の状態だと、Androidでそもそもマーカーの読み込みがうまくいきませんでした。
1つ目のマーカーは読み込むのですが、2つ目のマーカーにかざしてもオブジェクトが切り替わらないという現象がおきました。iOSではうまくうごいていたのですが!
そして、以下のif文を付け足すことで解決しました。
if (a.trackingState == UnityEngine.XR.ARSubsystems.TrackingState.Tracking)
検証してみた結果を書いておきます!
TrackingState
はTracking
、Limited
、None
があります。
・Tracking
:画像を追跡している状態
・Limited
:画像が"制限"とみなされている状況(??????)
・None
:画像が追跡されていない状況
このTrackingStatusを確認してみたところ、iOSでは画像に向けている時はTracking
と出続けているのですが、Androidでは1つ目のマーカーにかざしているときはTracking
と出続けているのですが、
2つめ以降のマーカーにかざした時、1フレごとに
TrackingStatus: Limited MarkerName: 1 //1つ前にかざしていたマーカーの番号
と
TrackingStatus: Tracking MarkerName: 2 //現在かざしているマーカーの番号
が繰り返されていました。
現在かざしているマーカーでトラッキングはされているのに、なぜかLimitedになったときに1つ前のマーカーが認識?されています。
このLimited
はUnityのAR Foundationドキュメント(をGoogle翻訳にかけたもの)には、
画像は追跡されていますが、追跡されていません。
画像が追跡ではなく制限と見なされる状況は、基礎となるARフレームワークによって異なります。 追跡が制限される原因となる可能性のある例は次のとおりです。画像がカメラに見えないようにぼかします。 画像は動画として追跡されません。 これは、たとえば、maxNumberOfMovingImagesを超えた場合に発生する可能性があります。
マーカーからカメラを背けているときは両方揃ってLimitedがでるのですが(ここでなんでNoneにならないのかもよくわからない)、他のところでLimitedのでかたがARKit XR Plugin と ARCore XR Pluginで違うらしく、AR Core Pluginではなぜか二つ目以降のマーカーをかざしたときに1フレごとにTrackingとLimitedで交互ででるようになっているっぽいです。
おそらく、一度でも認識した画像は以後、認識対象候補として常に存在していて、それが現在どういう状態か(かざしていたらTracking, かざしていないものはLimited)を教えてくれているということなのかなと思います。なので視界からはずれた一つ前のマーカーがLimitedになっているのかなと。iOSではLimitedがでなかったのは、「現在認識しているもの」のみに注目しているのかもしれないです。
で、LimitedとTrackingでそれぞれ認識しているマーカーが違うので2つ目以降のマーカーからオブジェクトが切り替わらなかったわけで、
if (a.trackingState == UnityEngine.XR.ARSubsystems.TrackingState.Tracking)
としてTrackingの瞬間をピックアップしてあげることでオブジェクトが出るようになったわけでした。
なんだよう。