第2章 ARKit 堤 修一/@shu223
2.8.1 ARKitにおける座標と現実のスケール
ARKitは現実空間の平面を認識して、3D空間における座標に落とし込みます。前述したとおり現実のスケールも推定するため、そのARKitの3次元空間における座標のスケールは、推定した現実のスケールを反映しています。その単位はメートルです。
たとえば、座標(0, 0, 0)にあるアンカーAと、座標(0, 0, 1)にあるアンカーBがARKitによって検出されたとすると、ARKitは、アンカーBはアンカーAに対してz方向に1メートル離れた位置にある、と推定していることになります。
ARSCNViewのhitTest(_:types:)メソッドによるヒットテスト注24)で得られるARHitTestResultのdistanceプロパティの値も、ヒットしたオブジェクトのカメラからの距離をメートルで表しています。
2.8.2 現実空間における二点間の距離
さて、冒頭で紹介したメジャーのアプリは、距離を計測したい始点と終点をそれぞれ画面タップで指定するという操作方法になっているようです。
ということは、ARKitのヒットテストの機能を用いれば、3D空間における2点間の座標の距離を計算する方法で同様のことが行えそうです。具体的なコードは次のとおりです。
// プロパティとして定義
var startNode: SCNNode? // 計測したい距離の始点を示すノード
var endNode: SCNNode? // 計測したい距離の終点を示すノード
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// タップ位置のスクリーン座標を取得
guard let touch = touches.first else {return}
let pos = touch.location(in: sceneView)
// 平面を対象にヒットテストを実行
let results = sceneView.hitTest(pos, types: [.existingPlane])
// 最も近い(手前にある)結果を取得
guard let result = results.first else {return}
// ヒットした位置を計算する
let hitPos = result.worldTransform.position()
// 始点はもう決まっているか?
if let startNode = startNode {
// 終点を決定する(終点ノードを追加)
endNode = putSphere(at: hitPos, color: UIColor.green)
guard let endNode = endNode else {fatalError()}
// 始点と終点の距離を計算する
let distance = (endNode.position - startNode.position).length()
// 始点と終点を結ぶ線を描画する
lineNode = drawLine(from: startNode, to: endNode, length: distance)
// ラベルに表示
statusLabel.text = String(format: "Distance: %.2f [m]", distance)
} else {
// 始点を決定する(始点ノードを追加)
startNode = putSphere(at: hitPos, color: UIColor.blue)
}
}