見出し画像

ARKit入門 / 平面の検出(1)

iOSの「ARKit」で「平面」を検出するプログラムを作ります。

1. 平面の検出

「平面」を検出し、検出領域を青い矩形で表示します。

画像1

import UIKit
import SceneKit
import ARKit

//平面の検出(1)
class ViewController: UIViewController, ARSCNViewDelegate {
   @IBOutlet var sceneView: ARSCNView!

   //ロード時に呼ばれる
   override func viewDidLoad() {
       super.viewDidLoad()
       
       //シーンの作成
       sceneView.scene = SCNScene()

       //特徴点とワールド原点の表示
       sceneView.debugOptions = [
           ARSCNDebugOptions.showFeaturePoints,
           ARSCNDebugOptions.showWorldOrigin]
       
       //光源の有効化
       sceneView.autoenablesDefaultLighting = true;
       
       //ARSCNViewデリゲートの指定
       sceneView.delegate = self
   }
   
   //ビュー表示時に呼ばれる
   override func viewWillAppear(_ animated: Bool) {
       super.viewWillAppear(animated)
       
       //コンフィギュレーションの生成
       let configuration = ARWorldTrackingConfiguration()
       
       //平面検出の有効化
       configuration.planeDetection = .horizontal
       
       //セッションの開始
       sceneView.session.run(configuration)
   }

   //ビュー非表示時に呼ばれる
   override func viewWillDisappear(_ animated: Bool) {
       super.viewWillDisappear(animated)
       //セッションの一時停止
       sceneView.session.pause()
   }
   
   //ARアンカー追加時に呼ばれる
   func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
       DispatchQueue.main.async {
           //ARPlaneAnchorの時
           if let planeAnchor = anchor as? ARPlaneAnchor {
               //位置とサイズによる平面ノードの追加
               let planeNode = SCNNode()
               let geometry = SCNPlane(
                   width: CGFloat(planeAnchor.extent.x),
                   height: CGFloat(planeAnchor.extent.z))
               geometry.materials.first?.diffuse.contents = UIColor(red: 0, green: 0, blue: 1, alpha: 0.5)
               planeNode.geometry = geometry
               planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2.0, 1, 0, 0)
               node.addChildNode(planeNode)
           }
       }
   }
   
   //ARアンカー更新時に呼ばれる
   func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
       DispatchQueue.main.async {
           //ARPlaneAnchorの時
           if let planeAnchor = anchor as? ARPlaneAnchor {       
               //位置とサイズの更新
               guard let planeNode = node.childNodes.first,
               let geometry = planeNode.geometry as? SCNPlane else {fatalError()}       
               planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
               geometry.width = CGFloat(planeAnchor.extent.x)
               geometry.height = CGFloat(planeAnchor.extent.z)
           }
       }
   }
}

◎光源の追加
追加するオブジェクトを表示するため、光源を有効化します。

       //光源の有効化
       sceneView.autoenablesDefaultLighting = true;

◎コンフィギュレーションの設定
コンフィギュレーションの設定で平面検出を有効化するには、planeDetectionに.horizontalを指定します。

       //平面検出の有効化
       configuration.planeDetection = .horizontal

コンフィギュレーションで可能な設定は次の通りです。

・planeDetection: 水平面(.horizontal)と垂直面(.vertical)を認識するかどうか
・detectionImages: 画像マーカーの指定
・detectionObjects: 3Dオブジェクトのマーカーの指定
・environmentTexturing: 環境マッピングの有効化
・isAutoFocusEnabled: カメラのオートフォーカスの有効化

◎ARSCNViewデリゲートの設定
ARアンカーとARアンカーの子ノードの同期を行うためのデリゲートです。

       //ARSCNViewデリゲートの指定
       sceneView.delegate = self
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)
ARアンカー追加時(新規に平面検出時)に呼ばれる。

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?
追加されたARアンカーに対応するノードを提供。

func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor)
ARアンカー削除時(平面検出解除時)に呼ばれる。

func renderer(_ renderer: SCNSceneRenderer, willUpdate node: SCNNode, for anchor: ARAnchor)
ARアンカーの位置をARアンカーに対応するノードに反映する前に呼ばれる。

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)
ARアンカーの位置をARアンカーに対応するノードに反映した後に呼ばれる。

◎位置とサイズによる平面ノードの追加
平面検出を有効にしているので、検出時には「ARPlaneAnchor」が「renderer(_:didAdd node:for anchor:)」に通知されます。可視化するために、SCNPlaneのジオメトリを持つ平面ノードを生成して追加します。

   //ARアンカー追加時に呼ばれる
   func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
       DispatchQueue.main.async {
           //ARPlaneAnchorの時
           if let planeAnchor = anchor as? ARPlaneAnchor {
               //位置とサイズによる平面ノードの追加
               let planeNode = SCNNode()
               let geometry = SCNPlane(
                   width: CGFloat(planeAnchor.extent.x),
                   height: CGFloat(planeAnchor.extent.z))
               geometry.materials.first?.diffuse.contents = UIColor(red: 0, green: 0, blue: 1, alpha: 0.5)
               planeNode.geometry = geometry
               planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2.0, 1, 0, 0)
               node.addChildNode(planeNode)
           }
       }
   }

◎位置とサイズの更新
検出した平面の情報更新の検出時には「ARPlaneAnchor」が「renderer(_:nodeFor anchor:)」に通知されます。検出情報に応じて位置とサイズを更新します。

   //ARアンカー更新時に呼ばれる
   func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
       DispatchQueue.main.async {
           //ARPlaneAnchorの時
           if let planeAnchor = anchor as? ARPlaneAnchor {       
               //位置とサイズの更新
               guard let planeNode = node.childNodes.first,
               let geometry = planeNode.geometry as? SCNPlane else {fatalError()}       
               planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)
               geometry.width = CGFloat(planeAnchor.extent.x)
               geometry.height = CGFloat(planeAnchor.extent.z)
           }
       }
   }

2. ARアンカー

検出したARアンカーの位置と向きは「anchor」、ARアンカーの情報を元に表示するノードは「node」として渡されます。

・anchor: 検出したARアンカーの位置と向き。
・node: ARアンカーの情報を元に表示するノードのジオメトリと位置と向きとスケール。

ARアンカーの種類は次の通りです。

・ARPlaneAnchor: 平面のARアンカー
・ARImageAnchor: 画像のARアンカー
・ARObjectAnchor: 3DオブジェクトのARアンカー
・AREnvironmentProbeAnchor: 環境照明のARアンカー
・ARFaceAnchor: 顔の表情のARアンカー(フロントカメラ)
・ARBodyAnchor: 身体の3D空間での動きを追跡するARアンカー
・ARParticipantAnchor: マルチユーザーARエクスペリエンスの別のユーザーを表すARアンカー


この記事が気に入ったらサポートをしてみませんか?