すだちキャンパス

すだちキャンパス

やってみたこと、学んだことなどのメモ。

6D.AIでオクルージョンカリングしてみた

こんにちは。6D.AIというSDKを使ってみたので、メモしておきます。

オクルージョンカリングとは

オクルージョンカリングとは、AR上で位置関係(手前と奥)を判別して、ARで表示したものが奥にある場合に手前の物と重なっている部分を描画しないことです。
iPhoneXS以降だとカメラの性能が上がったためARKit等を使うとこれが実装できるのですが、私はiPhone8なので残念ながらできません。しかし、6D.AIなら単眼カメラでもできる!ということでやってみました。

サンプルで遊んでみる

こちらに書いてある通りに進めましたが、一応日本語でも書いておきます。
まずはこちらから開発者登録をします。
次に、こちらでUnityなどのバージョンの要件を満たしているか確認します。2019年11月現在、必要な環境は次の通りです。

環境が整ったら、こちらからサンプル(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です。
f:id:sweetgohan:20191204235811p:plain

そしてボタンのインスペクタの下の方に行き、画像のように設定します。
f:id:sweetgohan:20191205000245p:plain
今回はボタンを2つ(meshHideButtonとmeshShowButton)作成し、片方を押したらメッシュの表示・非表示が切り替わりボタンも切り替わるようにしました。なので、meshShowButtomは初期状態では非表示にしてあります。
一応meshShowButtonの設定画像も載せておきます。
f:id:sweetgohan:20191205000301p:plain

また、ボタンが目立つと見栄えが悪いのでこちらの記事を参考にして枠線のみにしました。
さらに、ボタンを押した時はタッチイベントを無視する処理を加えて完成です。

//if (touch.phase == TouchPhase.Began) を次のように修正
if (touch.phase == TouchPhase.Began && EventSystem.current.currentSelectedGameObject == null)

XcodeiOSのバージョンを追加(おまけ)

この記事を書いている間に、Minecraft Earth のアーリーアクセス版がリリースされたため、遊ぶためにiOSを13.2にアップデートしました。しかし、Xcodeのバージョンが古く最新のiOSをサポートしていないというエラーが出たため、解決法をメモしておきます。(アップデートしろという話ですが)
とても簡単で、こちらから対応したいiOSのバージョンのzipをダウンロードするだけです。ダウンロードできたら、FinderのアプリケーションからXcode上で右クリックして「パッケージの内容を表示」を選択します。あとはContents > Developer > Platforms > iPhoneOS.platform > DeviceSupport の中にフォルダを置き、Xcodeを再起動したらOKです。