シーンビューのデリゲートを設定して水平面の検出を知らせてもらう
デバッグ表示をオンにしただけでは、もちろん実際の機能は何も追加されません。ARKitが検出した現実世界の座標を知るには、シーンビューのデリゲートメソッドを実装して、検出したことを知らせてもらい、求める情報を受け取る必要があります。
そのためには、まずこのプログラムのビューコントローラーを、シーンビューのデリゲート(ARSCNViewDelegate)に設定します。一般にデリゲートの設定は、ビューコントローラーの定義の先頭で、継承するUIViewControllerに続いて、準拠するプロトコルとして記述するのでした。
また、シーンビューのセッションの起動時に、面の検出機能を表すプロパティplaneDetectionに、.horizontalを設定して水平面の検出機能を有効にします。
ここで水平面というのは、厳密に言うと重力と垂直の面のことです。室内なら床やテーブル、屋外なら地面などが対象となります。これ以外の設定としては垂直面(.vertical)の検出もできます。
さらに、シーンビューのデリゲートとしてのメソッドが、このビューコントローラー内に実装されていることを示すために、シーンビューのdelegateプロパティにselfを代入しています。
これで準備ができたので、あとは実際のデリゲートメソッド、rendererのdidAddを実装します。
これは、新たな水平面が検出されたときに呼ばれるメソッドです。その中では、まずARPlaneAnchor、つまり検出した面のアンカーオブジェクトを取得します。このアンカーというのは、一般には錨とか留め具という意味ですが、ARKitのアンカーは検出した面の位置や向きといった座標情報を含んでいます。イメージとしては、一般的な意味と同様ですね。
今回のプログラムでは、このアンカーから得られた座標情報からシーンキットの平面(SCNPlane)を作成し、それをノードに変換してシーン全体のノードの子ノードとして追加しています。つまり、これによって検出した現実世界の水平面にぴったり重なるような面の3Dオブジェクトを表示するわけです。面の色は半透明の緑に設定しています。
このプログラムを室内で実行すると、床に薄い半透明のシートを敷いたような映像が表示されました。
面の検出は、状況によってなかなかうまくいかないこともあります。原理としては、カメラの動きに対する映像の変化のし方から特徴点を検出して面を割り出すようなかたちになるようです。iPadごとカメラ左右に動かしたり、iPadを持ったまま人間が移動したりすると、その際の情報の変化から面を検出できることが多いでしょう。
後から変化した検出面の情報を使って面の表示をアップデートする
iPadを持ったまま移動すると、面が検出しやすくなると書きました。その際には、カメラが移動することによって、それまで見えていなかった面が視野に入ってくることもあります。ここまでの例で示したrendererのdidAddメソッドは、面を最初に検出した際に呼ばれるものでした。これだけでは、その面がさらに広がっていることが分かっても、検出面のアンカー情報を、表示しているノードの情報に反映できません。そのためには、同じrendererのdidUpdateメソッドを実装します。
このメソッドの形は、上で示したdidAddメソッドとよく似ています。処理の内容も途中までは同じです。違うのは、上のdidAddでは、新たに検出した面からノードを作ってシーンのノードに追加していたのに対し、こんどは既存のノードを受け取って、その大きさや位置を更新しているところです。 このメソッドの追加によって、いったん検出した面が、その後のカメラの動きに応じて拡張されるようになります。
次回の予定
今回は、ARKitの面の検出機能によって、現実世界の床やテーブル、地面の座標情報を取得し、それに合わせて半透明の面のノードを描画しました。これだけでは、あまり面白い表示にはなりません。しかし、最初に述べたように、現実の面を検出することによって、それに合わせて非現実の3Dオブジェクトを配置することができるようになります。それでこそ、現実と非現実が融合した拡張現実が実現できるというものです。次回はそのあたりに踏み込む予定です。