ARKitでグラフィティアートをして、ARWorldMapで共有する


はじめに

はじめまして、drama(@1901drama)です!
ARを勉強するにあたり日本語ドキュメントが少なくて苦戦したので、
誰かの助けになれば...と思い、Qiitaデビューしました。少しでもARが普及すると嬉しいです。
※記載間違い等あれば教えてください。

ARkit 2.0の発表

もう5ヶ月程前ですが、2018/9にARKit2.0(iOS12)がリリースされましたね。

https://www.moguravr.com/arkit-2-0/

~追加された機能達~
・ARのマルチプレイが可能に
・ARオブジェクトの再利用が可能に
・イメージ認識やトラッキングサポートが拡大
・AR向けフォーマット「USDZ」に対応 など

つまり、AR体験を
・他人と同時に見れる(共有出来る)
・過去の情報を再現出来る
・専用アプリ以外(safariや他のデバイス)でも見れる ということに。

以前unityでARをやってたときよりも、めちゃくちゃ出来ることが増えていて驚きです。
これはもう電脳コイルのようなAR時代も近いのではないか!!? と思い ARを始めました。
今回はAR開発を初めて 一つ目に作ったものを紹介します。

出来たもの

グラフィティーアートをどこでも出来れば楽しそう!という考えから、ARでラクガキするSNSを作りました。
3Dオブジェクトを置いたり描いたり、つくったものを共有/評価 出来ます。

・置いたり、書いたり


・共有したり、評価したり、上書きしたり

手こずった点は山ほどある...のですが、
一先ず、このアプリの肝であるAR共有機能についてのみ解説します。

ARの共有方法

ARKitのARWorldMapを使っています。
https://developer.apple.com/documentation/arkit/arworldmap

ARWorldMapには、物理的な空間情報と空間の特徴点を記録したARanchorが含まれています。
ざっくりいうと、ARworldMap≒取得した空間内の位置情報群 と思って頂ければいいです。

このARWorldMapを利用し下図のような形で、空間情報の共有を実装しました。

・作成ユーザーを識別する為に、Authenticationを利用。
・空間情報(ARWorldMap)とオブジェクトの情報はサイズが大きいのでCloudStorageを利用。
・その他の情報は、Firestoreを利用。
 ※図のRealtime DBは、Firestoreに変更しました。

【処理の順番】
1.空間に置いたり描いたりしたオブジェクトと、空間情報(ARWorldMap)を紐づけて表示させます。

//空間にオブジェクトを置く時の処理
//SCNodeを作成し名前をつける。(細かい設定等は省略します)
let testNode= SCNNode()
testNode.name = "testNode"

//アンカーにSCNNodeと同じ名前をつけて、タッチした場所(空間)に追加する。
let Anchor = ARAnchor(name: "testNode", transform: hitTestResult.worldTransform)
sceneView.session.add(anchor: Anchor)
self.testNodes.append(testNode)
//空間にanchorが追加された時の処理
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
  //anchorと同じ名前のSCNNodeを空間に追加する。
  for testNode in testNodes{
    if anchor.name == testNode.name {
      sceneView.scene.rootNode.addChildNode(testNode)
    }
  }
}

2.空間情報(ARworldMap)と 置いたり描いたりしたオブジェクトの情報を アーカイブし、共有したい人がアップロードします。

//Apple公式
func writeWorldMap(_ worldMap: ARWorldMap, to url: URL) throws {
 let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true) try data.write(to: url) 
}
//①ARachor(ARworldMap)を取得→②ARachor(ARworldMap)をアーカイブ(シリアライズ)してdata化 →③dataをURLにアップロードの流れ
//アプリ内に記載したコード
func dataExport(){
    //AR_DATA(ARworldMap) Export
    self.sceneView.session.getCurrentWorldMap { (worldMap, error) in
        guard let worldMap = worldMap else { print("Error getting current world map."); return }
        do {
            self.ar_data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
        } catch let error as NSError { print("failed to write: \(error)")}
    }

    //OBJECT_DATA(オブジェクト) Export
    let objectData = self.testNodes
    do {
        self.object_data = try NSKeyedArchiver.archivedData(withRootObject: objectData, requiringSecureCoding: true)
    } catch let error as NSError { print("failed to write: \(error)") }
}

//この後 FirebaseのFirestoreにアップロードしてます。

3.最後に、共有されたい人がインポートすることで、空間を再現します。

//Apple公式
func loadWorldMap(from url: URL) -> ARWorldMap throws {
 let mapData = try Data(contentsOf: mapURL) guard let worldMap = try NSKeyedUnarchiver.unarchivedObject(of: ARWorldMap.classForKeyedUnarchiver(), from: mapData) as? ARWorldMap else { throw ARError.invalidWorldMap } return worldMap 
}
//④dataをURLからダウンロード→⑤dataをアンアーカイブ(デシリアライズ)してARachor(ARworldMap)化→⑥現在の環境にARachor(ARworldMap)を反映
//アプリ内に記載したコード
//この前に FirebaseのFirestoreからダウンロードしてます。(downloadsPath_AR_DATA,downloadsPath_OBJECT_DATA)
func dataImport(){
  //OBJECT_DATA(オブジェクト) Import
  let unarchievedOBJECT_DATA = try? NSKeyedUnarchiver.unarchiveObject(with:Data(contentsOf:downloadsPath_OBJECT_DATA))
  guard let downloadtestNodes:[SCNNode] = unarchievedOBJECT_DATA as? [SCNNode] else { return }
  for testNode in downloadtestNodes {
    self.testNodes.append(testNode)
  }

  //AR_DATA(ARworldMap) Import
  let unarchievedAR_DATA = try? NSKeyedUnarchiver.unarchiveObject(with:Data(contentsOf:downloadsPath_AR_DATA))
  guard let newWorldMap = unarchievedAR_DATA as? ARWorldMap else {return}

  //downloadしたARWorldMapを反映
  self.configuration.initialWorldMap = newWorldMap
  self.sceneView.session.run(self.configuration)
}

まとめ

AR開発は成果物が空間に目に見えて現れるのが面白いです。
普段はディレクションやサービス設計がメインなので、アプリ開発自体も楽しいです。

ここまで読んで頂きありがとうございました!
※今回のアプリは、ちゃんとしたキャラクターが完成すればリリースする予定です。