Swift Playgroundsで学ぶiOSプログラミング

ARKitを使って非現実世界との融合に備える

文●柴田文彦 編集●吉田ヒロ

2018年07月11日 17時00分

シーンビューのデリゲートを設定して水平面の検出を知らせてもらう

 デバッグ表示をオンにしただけでは、もちろん実際の機能は何も追加されません。ARKitが検出した現実世界の座標を知るには、シーンビューのデリゲートメソッドを実装して、検出したことを知らせてもらい、求める情報を受け取る必要があります。

 そのためには、まずこのプログラムのビューコントローラーを、シーンビューのデリゲート(ARSCNViewDelegate)に設定します。一般にデリゲートの設定は、ビューコントローラーの定義の先頭で、継承するUIViewControllerに続いて、準拠するプロトコルとして記述するのでした。

シーンビューが検出した面の情報を得るために、まずビューコントローラーをシーンビューのデリゲートに設定します。そのためには、まずARSCNViewDelegateのプロトコルに準拠させる必要があります

 また、シーンビューのセッションの起動時に、面の検出機能を表すプロパティplaneDetectionに、.horizontalを設定して水平面の検出機能を有効にします。

現実世界の水平面(床やテーブル、地面)を検出させるために、ARWorldTrackingConfigurationオブジェクトのplaneDetectionプロパティに、.horizontalを代入します。さらにシーンビューのdelegateプロパティにselfを代入し、このビューコントローラー内のメソッドが呼ばれるように設定します

 ここで水平面というのは、厳密に言うと重力と垂直の面のことです。室内なら床やテーブル、屋外なら地面などが対象となります。これ以外の設定としては垂直面(.vertical)の検出もできます。

 さらに、シーンビューのデリゲートとしてのメソッドが、このビューコントローラー内に実装されていることを示すために、シーンビューのdelegateプロパティにselfを代入しています。

 これで準備ができたので、あとは実際のデリゲートメソッド、rendererのdidAddを実装します。

rendererのdidAddメソッドを実装して、検出した水平面のアンカー情報を取得します。そこから面の大きさと位置の情報を取り出して、その面を新たなノードとして作成し、全体のノードに子ノードとして追加しています。検出した面は、なぜか垂直になっているので、90度回転してから追加しています

 これは、新たな水平面が検出されたときに呼ばれるメソッドです。その中では、まずARPlaneAnchor、つまり検出した面のアンカーオブジェクトを取得します。このアンカーというのは、一般には錨とか留め具という意味ですが、ARKitのアンカーは検出した面の位置や向きといった座標情報を含んでいます。イメージとしては、一般的な意味と同様ですね。

 今回のプログラムでは、このアンカーから得られた座標情報からシーンキットの平面(SCNPlane)を作成し、それをノードに変換してシーン全体のノードの子ノードとして追加しています。つまり、これによって検出した現実世界の水平面にぴったり重なるような面の3Dオブジェクトを表示するわけです。面の色は半透明の緑に設定しています。

 このプログラムを室内で実行すると、床に薄い半透明のシートを敷いたような映像が表示されました。

プログラムを動かして室内を撮影してみると床面が検出できました。テーブルの面も検出できます。検出されにくい場合は、ゆっくりとカメラの向き動かしたり、iPadを持ったまま移動してみましょう

 面の検出は、状況によってなかなかうまくいかないこともあります。原理としては、カメラの動きに対する映像の変化のし方から特徴点を検出して面を割り出すようなかたちになるようです。iPadごとカメラ左右に動かしたり、iPadを持ったまま人間が移動したりすると、その際の情報の変化から面を検出できることが多いでしょう。

後から変化した検出面の情報を使って面の表示をアップデートする

 iPadを持ったまま移動すると、面が検出しやすくなると書きました。その際には、カメラが移動することによって、それまで見えていなかった面が視野に入ってくることもあります。ここまでの例で示したrendererのdidAddメソッドは、面を最初に検出した際に呼ばれるものでした。これだけでは、その面がさらに広がっていることが分かっても、検出面のアンカー情報を、表示しているノードの情報に反映できません。そのためには、同じrendererのdidUpdateメソッドを実装します。

カメラの移動によって検出した面が拡張されることに対応するため、rendererのdidUpdateメソッドも追加で実装します。今度は検出面から新たなノードを作成するのではなく、更新された面の情報を既存のノードに反映させるだけです

 このメソッドの形は、上で示したdidAddメソッドとよく似ています。処理の内容も途中までは同じです。違うのは、上のdidAddでは、新たに検出した面からノードを作ってシーンのノードに追加していたのに対し、こんどは既存のノードを受け取って、その大きさや位置を更新しているところです。  このメソッドの追加によって、いったん検出した面が、その後のカメラの動きに応じて拡張されるようになります。

上のメソッドの追加によって、新たに検出された面がシーンに追加されるだけでなく、以前に検出された面の面積や位置が随時更新されるようになりました。検出された面を見ながら移動すると更新されやすいでしょう

次回の予定

 今回は、ARKitの面の検出機能によって、現実世界の床やテーブル、地面の座標情報を取得し、それに合わせて半透明の面のノードを描画しました。これだけでは、あまり面白い表示にはなりません。しかし、最初に述べたように、現実の面を検出することによって、それに合わせて非現実の3Dオブジェクトを配置することができるようになります。それでこそ、現実と非現実が融合した拡張現実が実現できるというものです。次回はそのあたりに踏み込む予定です。

mobileASCII.jp TOPページへ