ARKit基礎まとめ - 平面検出して3Dオブジェクトを置く


ARKit基礎

2018年もあと1時間で終了です。
今年は、ARのデモアプリをいくつか作ったりしましたが、AppStoreに出す程のアプリがまだ開発できていない事が心残りです。

来年こそ、ARKitを使いこなしていきたいと思っているのですが、UIKitほどまだ仲良くなれていない感じがするので、今更ながらARKitの基本的な使い方や作法をまとめて復習していきます。

平面検出して3Dオブジェクトを置く

デモで一番良くみる平面検出して、3Dオブジェクトを置くやつ。

最低限のコードで平面検出 + 3Dオブジェクト配置を実装するには以下の様な流れになります。

  • ARSCNViewの設定(水平面を検出する設定)
  • 3Dオブジェクト取り込み用のメソッドの準備(今回はdaeファイル)
  • ARSCNViewDelegateのメソッドで3Dオブジェクトを配置

ARSCNViewの設定

ViewController.swift
class ViewController: UIViewController {

    @IBOutlet var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the view's delegate
        sceneView.delegate = self

        // fps情報などを表示
        sceneView.showsStatistics = true

        // デバッグ用に特徴点を表示
        sceneView.debugOptions = ARSCNDebugOptions.showFeaturePoints

        //Viewに初期化したsceneをセット
        sceneView.scene = SCNScene()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Sessionの設定
        let configuration = ARWorldTrackingConfiguration()

        // .horizontalで水平面 (.verticalだと垂直面を検知)
        configuration.planeDetection = .horizontal

        //空間から光の情報を取得し画面上のライトの情報に適応
        configuration.isLightEstimationEnabled = true

        // sessionをスタート
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // sessionをストップ
        sceneView.session.pause()
    }

3Dオブジェクト取り込み用のメソッド

dae形式のファイルを取り込み
こちらの3Dモデルを利用させていただきました。
Beer bottle

ViewController.swift
    func load3dObject() -> SCNNode {
        let url = Bundle.main.url(forResource: "3d.scnassets/bottle", withExtension: "dae")!
        let sceneSource = SCNSceneSource(url: url, options: nil)!
        let virtualObjectNode
            = sceneSource.entryWithIdentifier("bottle", withClass: SCNNode.self)!
        return virtualObjectNode
    }

ARSCNViewDelegateのメソッドで配置

平面を新たに検知した際に、呼ばれるrenderer(_:didAdd:for:)で、検知したNodeに3DオブジェクトのNodeを追加

ViewController.swift
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        node.addChildNode(load3dObject())
    }

結果

シャドーやライトを調整しないとあまりリアリティが出ないですね。
あと、特徴量の少ない真っ白な床とかは、うまく検出できません。
アプリ化する際には、インストラクションをうまく作らないとユーザにストレスを与えてしまいそうです。