Apple Engine

Apple, iPhone, iOS, その周辺のことについて

ARKit 1.5 で平面認識した箇所からポリゴンを描画する

ARKit 1.5 から平面を認識した箇所からポリゴンを描画し、 以前の ARKit ではできなかった丸いテーブルなど若干ではあるが形状に合わせた平面を描画できるようになった。 今回はその解説。

 

ひとまず注意点

  • Beta 版であるため今後仕様が変更される可能性がある
  • Beta 版の Xcode 9.3 使用するため iOS Developer Program 加入している必要がある
  • 端末カメラを使用するため iOS 11.5 をインストールした実機が必要がある
  • 今回は動作部分しか書かないのでエラー処理など追加する必要がある

 

下準備

画像認識の方にも書いたけどこちらにも同じものを書いておく。

 

その1:シーンファイルを消す

既存の Xcode 同様に New Project(Command+Shift+N)から「Augmented Reality App」を選択し、新しいプロジェクトを作成。

今回は AR ではテンプレートとしてつくられる Scene File を使用しないので ViewController.swift の27行目を変更

こちらから

let scene = SCNScene(named: "art.scnassets/ship.scn")!

こちらに変更

let scene = SCNScene()

 

Scene File は必要ないので art.scnassets も消してもらっても構わない。

 

その2:デバッグオプションで特徴点を描画する

この状態が表示しても現実世界のトラッキングか可能にになっているかわからないので、デバッグオプションで特徴点の表示を設定する。 以下のコードを先ほどの 「let scene = 〜」の上らへんに書く

sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]

実機にビルド後、トラッキングが可能になると特徴点が黄色い点として画面に描画される。

 

その3:ARWorldTrackingConfiguration の平面認識設定の追加

viewWillAppear にある「let configuration = ARWorldTrackingConfiguration()」の下に、 以下のものを書き、水平と垂直の平面を認識できるようにする。

configuration.planeDetection = [.vertical, .horizontal]

ひとまず準備完了。

 

平面認識部分のコード

今回の流れ

  • ARKit のデリゲート didUpdate でトラッキングが変更されるたび処理する命令を書く
  • DispatchQueue 内で処理をかく
  • ARPlaneAnchor からアンカーを取得
  • Metal のデフォルトデバイスを設定し ARSCNPlaneGeometry でジオメトリを初期化
  • アンカーから認識した領域のジオメトリ情報を取得し ARSCNPlaneGeometry の update に処理を渡す
  • 半透明のマテリアルを設定
  • 設置しているノードへ処理したジオメトリを渡し描画する

このぐらいでリアルタイムに平面の認識を描画できるようになるのでかなり簡単。

 

以下のコード「// MARK: - ARSCNViewDelegate」 の下あたりに書いてビルドすると完成

// ARKit のデリゲート didUpdate でトラッキングが変更されるたび処理する命令を書く
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    
    // DispatchQueue 内で処理をかく
    DispatchQueue.main.async {
        
        // ARPlaneAnchor からアンカーを取得したり平面のノードを調べる
        if let planeAnchor = anchor as? ARPlaneAnchor {
            // Metal のデフォルトデバイスを設定する
            let device: MTLDevice = MTLCreateSystemDefaultDevice()!
            // ARSCNPlaneGeometry でジオメトリを初期化
            let plane = ARSCNPlaneGeometry.init(device: device)
            // アンカーから認識した領域のジオメトリ情報を取得し ARSCNPlaneGeometry の update に処理を渡す
            plane?.update(from: planeAnchor.geometry)
            
            // 60% 半透明の赤でマテリアルを着色
            plane?.firstMaterial?.diffuse.contents = UIColor.init(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.6)
            
            // 設置しているノードへ処理したジオメトリを渡し描画する
            node.geometry = plane
        }
        
    }
}

 

補足1:非同期

「DispatchQueue.main.async」で非同期の処理を設定しているがメインキューでやる必要性はないので、 普通のシリアルキューでも問題ないと思われる。
(新しい Apple のサンプルではメインキューで使用していない)

 

補足2:ARSCNPlaneGeometry

SCNGeometry のサブクラスなので、SCNGeometrySource と SCNGeometryElement で設定することができるが、
Metal のデフォルトデバイスで初期化し update で更新することができるので簡単。

 

以下の変更が何故か修正され、元に戻った。
次の Beta で変更される可能性あり。

ちなみに現状のドキュメントが変更されている。次の Beta 2 以降変更される可能性がある。
  • init > planeGeometryWithDevice:
  • update > updateFromPlaneGeometry

 

まとめ

以前より少しジオメトリの形状細かく取れるようになったので、 言われているように円のテーブルの平面を円形状に近いジオメトリで取得できるようになったため、 平面に置いた仮想オブジェクトが宙に浮くことが少なくなると思われる。

また、スプラトゥーンの様な領域を埋めた量で競うゲームを現実世界でできることもできるだろう。

 

サンプルコード

github.com