Google I/O 2018で発表された Cloud Anchors|ARCoreはiOSでも使えるということで、ARKitを使ったアプリを作っている身として早速触ってみました。
公式サンプルがobjcだったので、今回Swiftでオリジナルサンプルを作ってみました。以下にアップしたので参考にしてみてください。
本記事では、Cloud Anchorsの概要とサンプルの解説をしたいと思います。
Cloud Anchorsとは
(by Experience augmented reality together with new updates to ARCore)
現在、ARKit, ARCoreを使うと、カメラに写した世界の平面を認識し、空間を座標で表すことができます。しかし、他の端末で同じ場所を見た時に、**そこが同じ場所かどうかということはわかりません。**空間を座標で表し、平面を認識しただけだからです。
しかし、様々な企業が研究開発しているARCloudがあると、違う端末どうしが同じ場所を写した時に、そこが同じ場所であると認識できるようになります。
Cloud AnchorsはそのARCloudの簡易版と言えるでしょう。
空間から認識した特徴点を元にアンカーを生成し、その情報をGoogleのCloudを使って共有することができるフレームワークです。
With ARCore's Cloud Anchors, we've made it easy to build shared AR experiences. Works across Android and iOS. More here: https://t.co/mPmKzmPpcp pic.twitter.com/iafB1Rewzq
— Clay Bavor (@claybavor) 2018年5月8日
公式で公開されているサンプルは、ドロイドくんを平面上に配置すると、同じAPIKeyを参照するアプリで、同じ場所にドロイドくんを現すことができるというものです。
とりあえずiOSのARCoreのサンプル動かすマン #io18 #ARCore pic.twitter.com/YNYKl59IKM
— Daiki Matsudate@io18 (@d_date) 2018年5月9日
Original Sample
僕が簡単に作ったオリジナルサンプルを紹介します。
飛行機をおく | 同じ場所に飛行機が出現 |
---|---|
ちょっと向き変わっちゃってますけど(笑)、こんな感じで同じ場所に物体を出現させることができます。
手順はこんな感じです。
-
前提
-
アンカーが追加されたら飛行機出すようにする
-
送る側
-
アンカーを追加する(飛行機を置く)
-
アンカー情報をGoogleのCloudに送る
-
GoogleのCloudから帰ってきたcloudIdentifierをdatabaseに入れる
-
表示する側
-
databaseに入ったcloudIdentifierを受け取る
-
cloudIdentifierを使ってGoogleのCloudを検索する
-
アンカー情報を受け取ってアンカーを追加
-
アンカーが追加された場所に飛行機出てくる
解説していきます。
PlaneAnchor以外のアンカーだったら飛行機出すようにしとく
// MARK: - <#ARSCNViewDelegate#>
extension ViewController: ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
// Basicaly, you can get only ARPlaneAnchor,
// but this case, you can also get another anchor which is created by ARCore.
if !(anchor is ARPlaneAnchor) {
let scene = SCNScene(named: "art.scnassets/ship.scn")!
return scene.rootNode.childNode(withName: "ship", recursively: false)
}
return nil
}
}
基本的にARAnchor
って現状ARPlaneAnchor
しか配置されないので、ARPlaneAnchor
じゃなかったら飛行機を配置するようにしちゃいます。1
アンカー情報を送る
1.タップした平面にアンカーを追加
// Tap Event
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let touchLocation = touch.location(in: sceneView)
let hitTestResults = sceneView.hitTest(touchLocation, types: [.existingPlane, .existingPlaneUsingExtent, .estimatedHorizontalPlane])
if let result = hitTestResults.first {
addAnchor(transform: result.worldTransform)
}
}
// add an anchor to AR Space and share the anchor data to Google Cloud
private func addAnchor(transform: matrix_float4x4) {
let arAnchor = ARAnchor(transform: transform)
sceneView.session.add(anchor: arAnchor)
do {
_ = try gSession.hostCloudAnchor(arAnchor)
} catch {
print(error)
}
}
タップした平面にアンカーを追加し、その情報を try gSession.hostCloudAnchor(arAnchor)
でGoogleのCloudに共有します。
2.session:didHostAnchor
が呼ばれるので、databaseにcloudIdentifierを共有
func session(_ session: GARSession, didHostAnchor anchor: GARAnchor) {
let id = anchor.cloudIdentifier
firebaseReference.childByAutoId().child("hosted_anchor_id").setValue(id)
}
anchorのhostが終わると、GARSessionDelegateのsession:didHostAnchor
が呼ばれて、cloudIdentifier
を得ることができます。
ここではFirebaseRealtimeDatabaseでcloudIdentifier
を共有していますが、何を使ってもいいです。idが別の端末と共有できればいいので。
記録されたアンカー情報を受け取って表示
1.cloudIdentifierを受け取る
// fetch anchors from Firebase Database
private func observeAnchors() {
firebaseReference.observe(.value) { (snapshot) in
guard let value = snapshot.value as? [String : Any] else {
return
}
let list = value.values
list.forEach { value in
if let dic = value as? [String: Any],
let anchorId = dic["hosted_anchor_id"] as? String {
self.fetchedAnchorIds.append(anchorId)
}
}
}
}
サンプルでは、firebaseから受け取った情報を1回fetchedAnchorIds
にcloudIdentifier
に入れています。。この後ボタンを押した時にそのidentifierを使って同期することを試みます。
2.cloudIdentifierからGoogle Cloudを検索
private func resolveAnchors(){
fetchedAnchorIds.forEach { id in
_ = try! gSession?.resolveCloudAnchor(withIdentifier: id)
}
fetchedAnchorIds.removeAll()
}
ボタンのタップをトリガーに、resolveAnchors()
メソッドを呼び、try! gSession?.resolveCloudAnchor(withIdentifier: id)
で fetchedAnchorIds
を一個ずつGoogleのCloudに投げています。
3.発見するとdidResolveが呼ばれるので、アンカーを配置する
func session(_ session: GARSession, didResolve anchor: GARAnchor) {
let arAnchor = ARAnchor(transform: anchor.transform)
sceneView.session.add(anchor: arAnchor)
}
空間を見回していると、発見できます。
これで、アンカーをクラウドで共有することができました。
飛行機をおく | 同じ場所に飛行機が出現 |
---|---|
注意点
// MARK: - <#ARSCNViewDelegate#>
extension ViewController: ARSessionDelegate {
func session(_ session: ARSession, didUpdate frame: ARFrame) {
do {
try gSession.update(frame)
} catch {
print(error)
}
}
}
上のように、フレーム情報を常にARCoreに共有してください。そうじゃないとARCoreが空間を把握できないので。
まとめ
コードは https://github.com/kboy-silvergym/CloudAnchorsSwiftSampler に公開しました。Cloud AnchorsをSwiftで使う方の手助けになれば幸いです。
本家のサンプルよりもより無駄を削ぎ落として、CloudAnchorsが何をしているのか分かりやすく整理したつもりです。
では!
参考
- https://www.blog.google/products/google-vr/experience-augmented-reality-together-new-updates-arcore/
- https://developers.google.com/ar/develop/ios/overview
- https://github.com/d-date/arcore-ios-sdk/tree/swift-sample/Examples/CloudAnchorExampleSwift
-
...
ARImageAnchor
とARFaceAnchor
もあります ↩