Google I/O 2022で、ARCore Geospatial APIが発表されました。
Unityで使えるサンプルプロジェクトも公開されたので早速使ってみたのですが、これがものすごく良い!!
画期的すぎていろいろ夢が広がります。
これまでパブリックで使えるVPS(Visual Positioning System)は、Immersalなどが公開されていました。利用するには、位置合わせをしたい場所の写真を自分で大量に撮影し、マップを作成する必要がありました。
このマップを、Google Mapsでお馴染みのStreet Viewの情報を使って行ってしまうのがGeospatial APIです。
なので、Street Viewに公開されている場所なら自分でマップを生成することなしに、あらゆる場所で正確な位置合わせが行えてしまうわけです。
逆に言うと、Street Viewに公開されていない場所、例えば屋内などのプライベートな場所や機密性の高い場所で使用するには、Immersalなどマップを自分で作成できるサービスが依然として必要になります。
公開されていても、Geospatial APIの精度では足りない場合は同じく自分でマップを作ることができるサービスが必要ですね。
まずは、SDKとサンプルプロジェクトが動作する状態にします。
APIを使うためのKeyを取得する方法を含めて、必要下記ページにとてもわかりやすくまとまっています。
サンプルのGeospatialシーンや公式ドキュメントを見ていくと大体の使い方が見えてきます。
まず、Geospatial APIを使うときには、緯度・経度・高度の値が重要になります。
Geospatial APIでできる重要なことの1つが、プログラムが動いているスマホの「緯度・経度・高度・向き」がかなり正確にわかる、ということです。
これは高精度で高価な外付けのGPS(GNSS)を使ってもなかなか難しかったことで、これがスマホ単体で正確に速くわかってしまうのが本当に画期的です。
さらに、緯度・経度・高度を指定してオブジェクトを配置する、という機能もとても便利です。
これまでは端末の緯度経度が正確に分かったとしても、そこを基準にしてオブジェクトを配置するためには、面倒な緯度経度とUnity座標系の変換が必要でした。
Geospatial APIを使用すると、緯度・経度・高度・向きからUnity座標を簡単に算出してオブジェクトを配置することができます。
さっそくサンプルのGeospatialController.csで、オブジェクトを配置するメソッドを見てみましょう。
public void OnSetAnchorClicked() { var pose = EarthManager.CameraGeospatialPose; GeospatialAnchorHistory history = new GeospatialAnchorHistory( pose.Latitude, pose.Longitude, pose.Altitude, pose.Heading); if (PlaceGeospatialAnchor(history)) { _historyCollection.Collection.Add(history); SnackBarText.text = $"{_anchorObjects.Count} Anchor(s) Set!"; } (略) }
これは、端末の位置にオブジェクトを配置する、という流れなのですが、端末の位置は、EarthManager.CameraGeospatialPose から簡単に取得できることがわかります。
繰り返しになりますが、スマホ単体で正確にこの値が取得できるのは本当に素晴らしいです。
そして、オブジェクトを配置するために、PlaceGeospatialAnchorを呼び出しています。
private bool PlaceGeospatialAnchor(GeospatialAnchorHistory history) { Quaternion quaternion = Quaternion.AngleAxis(180f - (float)history.Heading, Vector3.up); var anchor = AnchorManager.AddAnchor( history.Latitude, history.Longitude, history.Altitude, quaternion); if (anchor != null) { GameObject anchorGO = Instantiate(GeospatialPrefab, anchor.transform); _anchorObjects.Add(anchorGO); return true; } return false; }
AnchorManager.AddAnchorに、端末の位置から作ったGeospatialAnchorHistoryを渡すことによってアンカーを作成し、そのアンカーのtransformを使ってPrefabをInstantiateしています。
これだけで、指定した緯度経度の位置にオブジェクトを配置できてしまうわけです。
サンプルを拡張して、ARナビアプリを作ってみました。
Geospatial APIで指定した場所までのナビ表示
ぶっちゃけGoogle Mapsのライブビュー機能と同じだけど、Unity上で自分で好きに作れるのがよい! pic.twitter.com/ruLLiXW3GX
— とりカシュ (@torikasyu) May 24, 2022
アンカーの配置方法はわかったので、あとはGoogle MapsのDirections APIの結果から道順の情報(ナビを行う緯度経度)を取り出して配置すれば作れます。
Directions APIの結果のJSONをUnity C#で扱うには、Visual Studioの自動生成機能で作ったクラスで、JsonConvert.Deserializeを行うと簡単です。
しかしながら、アンカーを配置する際に1点問題があります。それは「高度」です。
いま端末がある緯度・経度・高度は簡単に取得できますが、オブジェクトを遠くに配置する場合、そこの高度は何を指定すればいいのでしょうか?
単純化して、いまの端末の高さと同じ値を指定することも場合によってはアリですが、そうすると、地面の高さが違うので地面に埋まったり空中に浮いたりしてしまいます。
ドキュメントには、遠くのものは緯度経度から標高を求めよう、とあり、その方法が記載されています。
仕組み的には、緯度経度で異なる「ジオイド」と「標高」を組み合わせて算出を行います。
算出方法はドキュメントに記載されているものの他、幾つもやり方がありそうなのですが、今回はたぬ福さんに教えてもらったジオイドと標高を求める国土地理院のAPIを使用しました。
そのへん難しいですよね…こちらでやってるデモではこの2つのAPIを使って求めた標高+ジオイド高でやったらわりといい感じでした!
標高https://t.co/TCVkww0I16
ジオイド高https://t.co/D9shGxhCT5— たぬ福 (@tanufuku) May 25, 2022
上記ツイートにあるように、ジオイドと標高の値を緯度経度から算出し、足し算することでAddAnchorに指定する高度を求めています。
いろいろな要素があるので完全に正確ではなさそうなのですが、いまのところ自然に表示できています。
また、ジオイドの値はかなり広い範囲で変化がないので、オブジェクトを配置する範囲がそれほど広くなければ、最初の1回だけ取得して、その値を使い回すのでも大丈夫です(と社内勉強会で教えてもらいました)
以上のように、ちょっと触った感じですがGeospatial APIは本当に強力です。ARが日常で必須のツールになる日がぐっと近づいた気がしました!