6D.AIでオクルージョンカリングしてみた
こんにちは。6D.AIというSDKを使ってみたので、メモしておきます。
オクルージョンカリングとは
オクルージョンカリングとは、AR上で位置関係(手前と奥)を判別して、ARで表示したものが奥にある場合に手前の物と重なっている部分を描画しないことです。
iPhoneXS以降だとカメラの性能が上がったためARKit等を使うとこれが実装できるのですが、私はiPhone8なので残念ながらできません。しかし、6D.AIなら単眼カメラでもできる!ということでやってみました。
サンプルで遊んでみる
こちらに書いてある通りに進めましたが、一応日本語でも書いておきます。
まずはこちらから開発者登録をします。
次に、こちらでUnityなどのバージョンの要件を満たしているか確認します。2019年11月現在、必要な環境は次の通りです。
- iPhone 11, iPhone 11 Pro, iPhone 11 Pro Max, iPhone XS Max, iPhone XS, iPhone XR, iPhone X, iPhone 8 Plus, iPhone 8, iPhone 7 Plus, iPhone 7, iPad Pro (All generations and sizes), iPad Mini (2019, 5th Gen), iPad Air (2019, 3rd Gen), iPad (2019, 7th Gen), iPad (2018, 6th Gen) のいずれか
- iOS 11.4 or newer
- Xcode 10+
- Unity3D 2019.2+
- Unity Hub 2.0+
環境が整ったら、こちらからサンプル(Unity Sample App)をダウンロードします。また、こちらから自分のAPI Keyをダウンロードして、Assets>Plugins>iOS にある、SixDegreesSDK.plistを置き換えます。
以上完了したらUnityでサンプルを開き、プラットフォームをiOSに切り替えてビルドします。
あとはXcode projectを開き、Bundle Identifier (適当に入力)とTeam(サインインして名前を選択)を設定して実行します。
ちなみに、最近のXcodeはワイヤレスで実機転送できるんですね。XcodeのWindowからDevices and Simulatorsを選択し、Connect via networkにチェックを入れるだけです。純正のケーブルでないと、iPhoneを認識はしてくれても転送はできないみたいなので助かりました。
また、私のiPhoneはiOS11.3で止まっていたので、Deployment Targetのバージョンを下げてビルドしてみたのですが、上手く動きました。
注意
上でBundle Identifier (バンドルID)を適当に入力と書きましたが、実はこれは1週間で10個までしか新しく作れないようです。(適当に量産していたらいきなりエラーが出て焦りました)
例えば、「ai.6d.unitysampleeeee」のように適当に入力したら、しばらくそれを使い回すのが良さそうです。
3Dモデルを表示して簡単な操作をしてみる
準備
今度は自分で簡単なアプリを作って動かしてみることにします。タップで表示し、拡大縮小できるようにします。
まず、Main Cameraを消して6DSDK>Prefabs>のAR SceneとAR Backgroundを配置します。が、ここで注意点があります。
なぜかはわからないのですが、AR SceneのAR Meshのインスペクター上のチェックが外れて無いものになっていたので、チェックを入れましょう。
次に、3Dモデルを用意します。今回は、ニコニ立体ちゃんのVRMモデルを使用します。まずはVRMをUnityで扱うためにUniVRMをダウンロードします。そして、ニコニ立体ちゃんをダウンロードし、Unityにインポートします。おそらくですが、VRMモデルなら自作のものも使えると思います。
最後に、スクリプトを書きます。
タップした場所に表示
まずはタップした平面に表示するスクリプトです。こちらを参考にさせていただきました。
[SerializeField] private GameObject prefab; ... if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { //出現させる GameObject obj = Instantiate(prefab); Ray ray = Camera.main.ScreenPointToRay(touch.position); RaycastHit hit; if (Physics.Raycast(ray, out hit, 1 << SixDegrees.SDMesh.MeshLayer)) { obj.transform.position = hit.point + new Vector3(0f, 0f, 0f); obj.transform.LookAt(Camera.main.transform, Camera.main.transform.up); obj.transform.eulerAngles = new Vector3(0, obj.transform.eulerAngles.y, 0); } } }
さて、このままでは、タップした場所に次々と表示されて増殖してしまうので、 if (touch.phase == TouchPhase.Began) のif文の中に次のスクリプトを追加します。
また、GUIの方でプレハブにタグ(ここではalicia)を追加しておきます。
GameObject[] tagObjects; ... tagObjects = GameObject.FindGameObjectsWithTag("alicia"); if (tagObjects.Length > 1) { obj.name = "after"; } else { obj.name = "before"; } //1人だけにする処理 Debug.Log(tagObjects.Length); if (tagObjects.Length > 1) { GameObject kesu = GameObject.Find("before"); kesu.SetActive(false); Destroy(kesu); GameObject.Find("after").name = "before"; }
最初は単に
destroy(obj);
と書いていたのですが、これだとプレハブを消すことになってしまいエラーが出たので名前を検索して削除という形に変えました。
また、タグを使って名前をつけているのは、先に生成したオブジェクトから削除したいからです。(名前をつけないままだと、永久に最初のオブジェクトが残ってしまいました)
拡大・縮小
次に、ピンチイン・アウトで拡大縮小できるようにします。
float v = 0.6f; float vMin = 0.1f; float vMax = 1.3f; float EPSILON = 0.1f; ... if (Input.touchCount >= 2) { Touch t1 = Input.GetTouch(0); Touch t2 = Input.GetTouch(1); if (t2.phase == TouchPhase.Began) { dist = Vector2.Distance(t1.position, t2.position); } else if (t1.phase == TouchPhase.Moved && t2.phase == TouchPhase.Moved) { float newDist = Vector2.Distance(t1.position, t2.position); v = v + (newDist - dist) / 1000.0f * 5 / 3; if (v > vMax) { v = vMax; } else if (v < vMin) { v = vMin; } if (System.Math.Abs(v) > EPSILON) { GameObject.Find("before").transform.localScale = new Vector3(v, v, v); } } }
メッシュの表示非表示を切り替える
最後に、ARメッシュをボタンで非表示にできるようにします。これはもともと用意されている関数を使うだけで簡単にできます。
まずはUI > Canvas を作成して、その下にUI > Button を作成します。Canvasを作らずにButtonを作成すると、自動的にAR Background の下にCanvasとButtonが置かれてしまいボタンが表示されないので注意して下さい。
次に、作成したCanvasにComponentsを追加します。Add Component から、Event System と Standalone Input Module を選択します。画像のようになっていればOKです。
そしてボタンのインスペクタの下の方に行き、画像のように設定します。
今回はボタンを2つ(meshHideButtonとmeshShowButton)作成し、片方を押したらメッシュの表示・非表示が切り替わりボタンも切り替わるようにしました。なので、meshShowButtomは初期状態では非表示にしてあります。
一応meshShowButtonの設定画像も載せておきます。
また、ボタンが目立つと見栄えが悪いのでこちらの記事を参考にして枠線のみにしました。
さらに、ボタンを押した時はタッチイベントを無視する処理を加えて完成です。
//if (touch.phase == TouchPhase.Began) を次のように修正 if (touch.phase == TouchPhase.Began && EventSystem.current.currentSelectedGameObject == null)
XcodeにiOSのバージョンを追加(おまけ)
この記事を書いている間に、Minecraft Earth のアーリーアクセス版がリリースされたため、遊ぶためにiOSを13.2にアップデートしました。しかし、Xcodeのバージョンが古く最新のiOSをサポートしていないというエラーが出たため、解決法をメモしておきます。(アップデートしろという話ですが)
とても簡単で、こちらから対応したいiOSのバージョンのzipをダウンロードするだけです。ダウンロードできたら、FinderのアプリケーションからXcode上で右クリックして「パッケージの内容を表示」を選択します。あとはContents > Developer > Platforms > iPhoneOS.platform > DeviceSupport の中にフォルダを置き、Xcodeを再起動したらOKです。
感想
たまにずれていたりしますが、かなりの精度で感動しました。
実はこのようなアカウントを作って遊んでいるのですが、さらに捗りそうです。できれば、ボーンを動かせるようにしたいですね。最低限ポーズを切り替えられるようにはしてみたいので続きの記事を書くかもしれません。
それではまた。
参考記事
Xcodeでの実機転送をワイヤレスで行う方法 - Qiita
AR技術者必見!最新ARSDK『6D.AI』で遊んでみた | イケメンテックラボブログ
Unityの[SerializeField]について色々な疑問に答えてみる - Qiita
[Unity] PrefabからInstantiateしたときに文字列「(Clone)」が付加される問題 | ftvlog
【Unity】タグが付いたオブジェクト数を数える - Qiita
Unityでマルチタッチ入力を扱う - TASOGARE GAMES BLOG
Unityでピンチイン・ピンチアウトを実装する - Qiita
【Xcode】実機で動かそうとしたらこんなエラーが出た時 - Qiita
バンドルID(Bundle identifier)とは - Qiita
【Unity】ボタンの下にあるオブジェクトへのタップイベントを無効化する - zawazawa雑記
[Unity]EventSystem.current.IsPointerOverGameObject()でタッチ処理をスキップする覚書 - Qiita