[iOS 11][ARKit] 平面の検出について #WWDC2017

2017.08.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

iOS 11で追加されたARKitでは、水平の平面を検出することが可能です。(垂直は未対応[2017/08.26現在]) そして、この機能を試した動画が、Youtubeに公開されています。

今回は、このような平面の検出について確認してみました。

本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。

2 ARAnchor(ARPlaneAnchor)

ARAnchorとは、ARシーンにオブジェクトを配置するために使用する実世界の位置と方向を持ったオブジェクトです。 そして、これを継承したARPlaneAnchorは、特に平面を表現するものです。


Documentation > ARKit > ARAnchor
Documentation > ARKit > ARPlaneAnchor

ARKitでは、ARシーンを初期化するARWorldTrackingConfigurationplaneDetectionPlaneDetection.horizontalがセットされると、平面を検出した時点で、自動的にARPlaneAnchorがシーンに追加されるようになります。

let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal // <= 平面の検出を有効化する
sceneView.session.run(configuration)

3 アンカーの検出

ARPlaneAnchorが検出され、自動的にシーンに追加される時、ARSCNViewDelegaterenderer(_:didAdd:for:)が呼ばれます。

optional func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)

しかし、最初に検出した時点では、その平面情報(位置や大きさなど)は、あまり正確ではありません。しかし、しばらくカメラを動かしていると、その情報は、どんどん正確になって行きます。

情報がの正確さが向上するたびに、ARPlaneAnchorが更新され、この時、同じくARSCNViewDelegateのデリゲートメソッドである、renderer(_:didUpdate:for:)が呼ばれます。

optional func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)


Documentation > ARKit > renderer(_:didAdd:for:)
Documentation > ARKit > renderer(_:didUpdate:for:)

4 平面ノード

ARAnchorがシーンに追加されても、それをカメラで見ることはできません。そこで、ARAnchorの情報に基づいて、平面を表現するノードオブジェクトを作成してみます。

下記のクラスは、SCNNodeを継承した、平面を表現するノードオブジェクトです。シーンに映る、実世界のオブジェクトと重なっても分かりやすいように、半透明の白色になっています。

また、ARAnchorの情報で、その位置情報などを更新するメソッド update(anchor:)を持っています。

class PlaneNode: SCNNode {
    init(anchor: ARPlaneAnchor) {
        super.init()

        geometry = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))
        let planeMaterial = SCNMaterial()
        planeMaterial.diffuse.contents = UIColor.white.withAlphaComponent(0.5)
        geometry?.materials = [planeMaterial]
        SCNVector3Make(anchor.center.x, 0, anchor.center.z)
        transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)
    }

    func update(anchor: ARPlaneAnchor) {
        (geometry as! SCNPlane).width = CGFloat(anchor.extent.x)
        (geometry as! SCNPlane).height = CGFloat(anchor.extent.z)
        position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
    }

    // ・・・略・・・
}

5 ノードの追加及び、更新

ARSCNViewDelegateのメソッドで、上記の平面ノードを追加したり、更新したりするコードは、下記の通りです。

// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    DispatchQueue.main.async {
        if let planeAnchor = anchor as? ARPlaneAnchor {
            // 平面ノードを追加する
            node.addChildNode(PlaneNode(anchor: planeAnchor) )
        }
    }
}

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    DispatchQueue.main.async {
        if let planeAnchor = anchor as? ARPlaneAnchor, let planeNode = node.childNodes[0] as? PlaneNode {
            // 平面ノードの位置及び形状を修正する
            planeNode.update(anchor: planeAnchor)
        }
    }
}

6 最後に

今回は、ARKitで平面を検出する機能について、試して見ました。 起動直後に、直ちに検出できるわけではありませんが、しばらくカメラを動かしていると、検出の情報はどんどん正確になって行くのがわかります。

検出状況の体感ですが、平面全体が画面に収まる、例えば「机」などでは、比較的早く正確に検出できます。しかし、全体がなかなか映せない「床」などでは、検出に少し時間がかかるようです。

試したコードは、下記に置きました。不明な点があればご参照ください。
github [GitHub] https://github.com/furuya02/ARKitPlaneDetectionSample

7 参考リンク


Introducing ARKit
Apple Developer > Documentation > ARKit
WWDC2017 Session 602 Introducing ARKit: Augmented Reality for iOS
ARKit By Example — Part 2: Plane Detection + Visualization
[iOS 11] はじめてのARKit #WWDC2017
[iOS 11][ARKit] 距離の計測について #WWDC2017