LoginSignup
18
9

More than 5 years have passed since last update.

ARKitで「非」矩形な平面を検出する

Last updated at Posted at 2018-05-20

iOS 11.3(いわゆるARKit 1.5)より、任意の2次元形状のジオメトリで平面を検出できるようになりました。たとえば次のgifのように、丸テーブルの平面を円形(に近い)ジオメトリとして検出できます。

irregular.gif

なお、これから解説するAPIを見るとわかりますが、水平面だけでなく、ARKit 1.5で検出可能になった垂直平面にも本機能は有効です。

ARSCNPlaneGeometry

本機能において鍵となるのはARSCNPlaneGeometryというiOS 11.3(ARKit 1.5)の新クラスです。

SCNGeometryを継承しており、ARKitが検出した平面の2次元形状を表すためのクラスです。

このクラスのイニシャライザはひとつしかなく、

init?(device: MTLDevice)

このように、MTLDeviceオブジェクトを渡すようになっています。つまり、Metalサポート必須ということです。

Screen Shot 2018-05-20 at 16.27.44.png

ARSCNPlaneGeometry is available only in SceneKit views or renderers that use Metal. This class is not supported for OpenGL-based SceneKit rendering.

個人的には、こういうAPIで明示的にMetalのクラス(MTLDeviceはプロトコルですが)が必要になるというのも珍しいなと思いました。たとえばCore MLは内部的にMetalを用いていても一切Core MLフレームワークのAPIには出てきませんし、SceneKitやCore Imageも明示的にMetalを利用する部分以外では出てきません。

ARPlaneAnchorのgeometryプロパティ

iOS 11.3でARPlaneAnchorgeometryというプロパティが追加されました。型はARPlaneGeometryです。

ARPlaneGeometryはARKitが検出した平面の形状のメッシュ情報を保持するクラスです。プロパティから、頂点座標や頂点インデックスにアクセスできます。

@property(nonatomic, readonly) const vector_float3 *vertices;
@property(nonatomic, readonly) const int16_t *triangleIndices;

なお、こちらの親クラスはNSObjectであり、SCNGeometryではありません。つまりこれはSceneKitのノードにジオメトリとしてそのまま割り当てられるものではないということです。ARAnchorSCNNodeではない、というのと同様に考えればいいでしょう。

このオブジェクトをARSCNPlaneGeometryupdate(from:)メソッドに渡すことでジオメトリを更新できます。

func update(from: ARPlaneGeometry)

実装方法

ARSCNPlaneGeometryを初期化する

MTLDeviceオブジェクトを作成し、ARSCNPlaneGeometryを初期化します。

let device = MTLCreateSystemDefaultDevice()!
planeGeometry = ARSCNPlaneGeometry(device: device)!

MTLDeviceやそのデフォルトデバイスについては「Metal入門」で解説しています。

平面検出時/更新時にARSCNPlaneGeometryを更新する

平面検出時/更新時に、ARPlaneAnchorオブジェクトのgeometryプロパティから取得したARPlaneGeometryオブジェクトを渡してARSCNPlaneGeometryオブジェクトを更新します。

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else {fatalError()}
    planeGeometry.update(from: planeAnchor.geometry)
    ...
}

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else {fatalError()}
    planeGeometry.update(from: planeAnchor.geometry)
}

ARKit-Samplerにサンプルコード入ってます&すぐにビルドして試せます。

以上。

18
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
9