イケメンテックラボでエンジニアをしている寺林です。
今回は前回の「UnityでAR -ARCoreを使う-」に続いてARネタということで、最近登場し一部界隈で話題の6D.AIを導入してみたのでその紹介をします!
6D.AIとは

公式サイト https://www.6d.ai/
6D.AIはスマートフォンのカメラ画像から3次元空間情報を高速で解析することのできるARのSDKです。ごく普通のスマートフォン単眼カメラでも高精度なAR体験を作り出すことができます。
動画を見るとわかるのですが、6D.AIではオクルージョンカリングに対応しています。しかも精度が非常に高いです。
オクルージョンカリングとはオブジェクトの手前などに何か物体が来た時(オブジェクトが見えなくなった時)に描画をオフにするという技術です。
これを実現するには、カメラで現実の空間を写しているのでカメラの映像から「現実世界の物がどの位置にあるか」や「手前に遮蔽物はあるのか」といった情報を判断する必要があります。
しかしOS上では画像はただの色情報の集まりでしかないので物の識別はとても難しい処理になります。
実際、ARKitやARCoreといった既存のARSDKではまだサポートされていなかったりと、実現するのが非常に難しい技術です。
6DAIではその問題を解決しており、かなり高い精度でカリングをしてくれています。これにより完全にその位置に存在するようなコンテンツを作ることが可能となります。
まだ出たばかりでBetaですが最新技術なのでどんどん触っていきましょう。
今回はこの6D.AIをサンプルビルドして少し弄ってみたいと思います。
必要な開発環境
ARCoreの時と同様に今回も使える端末に限りがあります。
2019年2月現在で対応している端末は
iPhoneXSMax,XS,XR,X,8Plus,8,7Plus,7
iPadPro(全世代),iPad2018
でiOS12、または11.4以降が必要です。
XCodeは10以上、Unityは2018.2以上であることが要件となっています。
情報は随時更新されると思うので公式のドキュメントページを都度確認してください。
また今回の記事はUnityをある程度使ったことがある方でiOSビルドの経験がある方向けの記事とさせていただきます。
記事執筆時の環境
Unity2018.3.6f1
XCode10.1
検証端末 iPhoneXR(iOS12.0.1)
デベロッパー登録
6D.AIは現在ベータ版のため、開発者登録が必要です。 https://dashboard.6d.ai/user/dashboard/?view=beta_signup
こちらから開発者登録をします。
必要事項を入力し登録しましょう。

登録するとメールアドレスに仮パスワードが届くのでそれを使ってDevelopperPortalにログインしましょう。
SDKとAPIキーのDL
ログインしたらページ右上のDownloadsからDownload Unity SDK SamplesとDownloadYour API KeysをクリックしUnityパッケージとAPIキーをダウンロードします。

2019年2月現在、6DSDK.0.20.0.UnitySampleApp.zipとSixDegreesSDK.plistがダウンロードされます。
ダウンロードしたら6DSDK.0.20.0.UnitySampleApp.zipを解凍します。
中身は普通にUnityのプロジェクトなのですが開く前にやることがあります。
DLしたSixDegreesSDK.plistを6DSDKUnitySampleApp/Assets/Plugins/iOS/SixDegreesSDK.plist と置き換えてください。APIを利用するのに必要な作業になります。

上書きしたらプロジェクトを開く準備は完了です。
ビルドしてみる
Unityで6DSDKUnitySampleAppディレクトリを指定しプロジェクトを開きましょう。プラットフォームは今のうちにiOSにしておきましょう。(開いてからSwitchPlatformしてもOKです)

開くとこんな感じでScenePickerシーンが開かれた状態になると思います。
とりあえずビルドしてみましょう。

Build Settingsを開き以下の5つのシーンがビルド対象になっていることを確認し、BuildAndRunを押しましょう。ビルド対象になっていない場合は追加してやってください。

保存場所と名前を指定します。今回は6dtestにしました。

Saveを押してしばらく待つとUnityでのビルドが終わり、XCodeのプロジェクトが立ち上がります。デバイスを接続しビルド先のデバイスを選択します。

次にorganizationと署名設定をしましょう。
左側にあるUnity-iPhoneをクリックするとこの画面になるので、Bundle Identifierに独自のドメイン名を入れます。
SigningのTeamも自分のアカウントを指定してやります。この辺りは人によって違うと思うので詳細は省きます。

次に上部Capabilitiesを選択し、下の方にあるWireless Accessory Configurationをオンにします。6D.AIではAR空間の共有機能を提供しており、それを利用するためにインターネットアクセスが必要になります。

最後に、6D.AIが推奨している設定としてEditSchemeのなかのDebugExecutableのチェックを外します。
これをすることで最適なパフォーマンスが得られるらしいのでとりあえずオフにしちゃいましょう。


設定が終わったらビルドをかけましょう。

ビルドが終わったら実機に転送されます
サンプルアプリで遊んでみた
アプリを起動すると権限の確認ダイアログが出るので許可しましょう。

アプリを起動するとこんな画面になります。カメラからの画像と上の方に4つのボタンが表示されます。
試したい機能のボタンを押すとサンプルが表示されるようになっています。

一通り触ってみましょう
Drawing Sample scene

AR空間上に絵が描けるサンプルです。
二つあり、一つはPhotonを使ったAR空間の共有が可能なようです。
空間に絵を描くという体験ができます。シンプルですがとても面白いです。いろんな方向から見たくなりますね。
Basic Sample scene

基本的な機能を体験できるサンプルです。
モデルを移動させたり、オクルージョンカリングの体験ができます。
また認識しているエリア情報をアップロード・ダウンロードすることが可能です。これにより煩わしい平面検知を削減できます。
少しはみ出たりしてますが概ね現実に沿ってカリングできてます。iPhoneXRの単眼カメラで出来るのは凄いですね
Ball Pit Sample scene

AR空間上にボールを投げることができるサンプルです。
こちらもカリングが行われます。
ボールがちゃんと椅子の上に乗っているのがわかると思います。椅子の裏に落ちたボールも見る位置を変えると見えるのでカリングもちゃんとされています。
以上が6DAIのサンプルアプリになります。触ってみるととても強力なARプラットフォームであることがわかったと思います。
ではどのようにこのARシーンを開発していくのかを紹介します。
6D.AIを使ったシーンを作ってみる
サンプルを触り、大体何ができるかはわかったので実際にUnityでアプリを作ってみましょう。
今回は簡単に、「タップしたところにUnityちゃんが表示されるアプリ」を作ってみます。完成イメージはこんな感じです。
シーン準備
先ほどのサンプルプロジェクトを開き新しくSceneを作成します。
今回は6dTestSceneとしました。

まず、MainCameraを削除します。

次にAssets/6DSDK/Prefabs/の中のAR Background と ARSceneの二つのPrefabをHierarchyに持って行きます。

これだけでセットアップは完了です。びっくりするくらい簡単です。
一度これでビルドしてみましょう。カメラからの映像が表示されればOKです。

起動して端末を少し動かしてやるとこのように色のついたメッシュが生成されます。
これが6D.AIが認識している平面の情報になります。 色は恐らくですが高さを表しています。

やることとしてはこの平面の上にモデルを置く感じになります。
Unityちゃんインポート
今回はUnityちゃんを表示させたいのでUnityちゃんをインポートしましょう。
AssetStoreからUnity-chanで検索しインポートします。


© Unity Technologies Japan/UCL
タップしてUnityちゃんを置くスクリプトを書く
まずは空のGameObjectを作りましょう

スクリプトを作成し作ったGameObjectに追加します。
今回はObjectGeneratorという名前にしました。

ではスクリプトを書いて行きましょう
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ObjectGenerator : MonoBehaviour { [SerializeField] private GameObject prefab; // Update is called once per frame void Update() { if (Input.touchCount > 0) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { Ray ray = Camera.main.ScreenPointToRay(touch.position); RaycastHit hit; if (Physics.Raycast(ray, out hit, 1 << SixDegrees.SDMesh.MeshLayer)) { GameObject obj = Instantiate(prefab); obj.transform.position = hit.point + new Vector3(0f, 0.02f, 0f);//そのまま出すと床に埋まってしまったので少し上に出す obj.transform.LookAt(Camera.main.transform, Camera.main.transform.up); obj.transform.eulerAngles = new Vector3(0, obj.transform.eulerAngles.y, 0); } } } } }
かなりシンプルなコードになります。
Updateの中でタップを検知してレイを飛ばしてぶつかった場所にオブジェクトを生成しているだけです。
一番大事なのはこの部分になります。
if (Physics.Raycast(ray, out hit, 1 << SixDegrees.SDMesh.MeshLayer)) { GameObject obj = Instantiate(prefab); obj.transform.position = hit.point + new Vector3(0f, 0.02f, 0f);//そのまま出すと床に埋まってしまったので少し上に出す obj.transform.LookAt(Camera.main.transform, Camera.main.transform.up); obj.transform.eulerAngles = new Vector3(0, obj.transform.eulerAngles.y, 0); }
Physics.Raycastの第三引数で衝突対象にするレイヤーを指定しているのですがSixDegrees.SDMesh.MeshLayerというものを指定しています。
オブジェクトのLayerを見てみると9番目にARMeshというものがあります。

SixDegree.SDMesh.MeshLayerの定義を見てみるとこちらも9になっています。

6D.AIでは検知した平面はそのままメッシュコライダーとしてUnityのワールド上に配置されています。
先ほど配置したARScene PrefabのなかにARMeshというオブジェクトがあり、その中で指定されているARMeshChunkというオブジェクトが表示されるメッシュを表しています。

このARMeshChunkにはMeshColliderが入っており、レイヤーがARMesh(9)に設定されているためPhysics.Raycastの衝突対象になるという訳です。

6D.AIではこのような仕組みになっている為、専用のヒットテスト系の関数などが無く、ほぼUnity標準の機能で実装ができます。
(実際、ボールを投げるサンプルもRigidbodytつけてAddForceしてるだけだったりします。)
さて、スクリプトを書いたら保存をしUnityへ戻りましょう。
スクリプトを追加したGameObjectにPrefabというフィールドが増えているのでそこにUnityちゃんを設置しましょう。

今回はAssets/unity-chan!/Unity-chan!Model/Prefabs/unitychanを指定しています。
これで完成です!
早速ビルドしてみましょう!
実行!
ビルドして平面に向かってタップするとUnityちゃんが表示されれば成功です!
ちゃんとカリングもされます
まとめ
6D.AIを軽く触ってみました。 実際に触ると驚くほど簡単に精度の高いAR体験を作り出せました。ほとんどUnityの機能だけで作れちゃうのも学習コスト低くて良いですね。
カリングがされることによりUnityちゃんが現実世界にいる感じ、『そこにいる感』が一気に強くなりました。まだSDK自体開発途中なので情報も少ないですがこれからさらに改良されていくと思うので今後の展開がとても楽しみです!
次回以降もAR系で何か紹介したいと思いますのでよろしくお願いします!
おまけ
平面検知している時に出てくるこのメッシュですが正直あんまり見栄えがよくないです。
非表示にしたい時もあると思います。

実はこれちゃんと消す関数が用意されています。
SDMeshクラスの中にあるShowMesh()で表示、HideMesh()で非表示になります。
なので適当にUIでボタンを作ってやり指定することで非表示ボタンをコード書かずに作ることができます。

こういう関数が最初から用意されてるのも嬉しいですね