iOS 11.3(いわゆるARKit 1.5)より、任意の2次元形状のジオメトリで平面を検出できるようになりました。たとえば次のgifのように、丸テーブルの平面を円形(に近い)ジオメトリとして検出できます。
なお、これから解説するAPIを見るとわかりますが、水平面だけでなく、ARKit 1.5で検出可能になった垂直平面にも本機能は有効です。
ARSCNPlaneGeometry
本機能において鍵となるのはARSCNPlaneGeometry
というiOS 11.3(ARKit 1.5)の新クラスです。
SCNGeometry
を継承しており、ARKitが検出した平面の2次元形状を表すためのクラスです。
このクラスのイニシャライザはひとつしかなく、
init?(device: MTLDevice)
このように、MTLDevice
オブジェクトを渡すようになっています。つまり、Metalサポート必須ということです。
ARSCNPlaneGeometry is available only in SceneKit views or renderers that use Metal. This class is not supported for OpenGL-based SceneKit rendering.
ARKit 1.5から使えるようになった「非」矩形なジオメトリとして平面検出できるやつ、Metalベースでレンダリングしてるときだけ使える。OpenGLベースでレンダリングしてる場合は使えない。ちょっとずつMetalしかサポートされない機能が増えてきた。 / ARSCNPlaneGeometry https://t.co/gUC4IA6K3i
— Shuichi Tsutsumi (@shu223) 2018年4月7日
個人的には、こういうAPIで明示的にMetalのクラス(MTLDeviceはプロトコルですが)が必要になるというのも珍しいなと思いました。たとえばCore MLは内部的にMetalを用いていても一切Core MLフレームワークのAPIには出てきませんし、SceneKitやCore Imageも明示的にMetalを利用する部分以外では出てきません。
ARPlaneAnchorのgeometryプロパティ
iOS 11.3でARPlaneAnchor
にgeometry
というプロパティが追加されました。型はARPlaneGeometry
です。
ARPlaneGeometry
はARKitが検出した平面の形状のメッシュ情報を保持するクラスです。プロパティから、頂点座標や頂点インデックスにアクセスできます。
@property(nonatomic, readonly) const vector_float3 *vertices;
@property(nonatomic, readonly) const int16_t *triangleIndices;
なお、こちらの親クラスはNSObject
であり、SCNGeometry
ではありません。つまりこれはSceneKitのノードにジオメトリとしてそのまま割り当てられるものではないということです。ARAnchor
はSCNNode
ではない、というのと同様に考えればいいでしょう。
このオブジェクトをARSCNPlaneGeometry
のupdate(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にサンプルコード入ってます&すぐにビルドして試せます。
以上。