ARKit+SceneKitでオブジェクトを配置するときのカーソル表示
ARで任意のオブジェクトを配置するときなどに表示するカーソルについて。
やりたいこと
カーソルのtransform
画面中央座標のヒットテストを行い、取得したworldTransform
をもとに設定する。
ARSCNView
の hitTest(_:types:)
でヒットした場所を ARHitTestResult
の worldTransform
から取得できるので、これをカーソルの場所・姿勢とする。
ここで、カーソルを SCNPlane
にした場合に、カーソルの場所・姿勢=ヒットした場所・姿勢 としてしまうと平面のジオメトリと干渉してチラつくので、平面の上方向のベクトル worldTransform.columns.1
を使ってカーソルの位置を調整する。
// 平面が向いている方向(UP)に0.01mずらした位置にカーソルを設定
cursorTransform.columns.3 += worldTransform.columns.1 * 0.01
self.cursorNode.simdTransform = cursorTransform
transform を設定するのでSCNNode
が拡大縮小(scale
≠ (1.0,1.0,1.0))されている場合、拡大縮小がリセットされる点に注意。
その場合は worldTransform.columns.0 ~ 2 に拡大縮小率を掛けた値を、cursorTransform.columns.0 ~ 2 に設定する。
出来上がり
カーソルの上方向が分かり易いようにピラミッド形状にしている。
ソースコード
import ARKit
import SceneKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet weak var scnView: ARSCNView!
private let device = MTLCreateSystemDefaultDevice()!
private let cursorNode = SCNNode()
override func viewDidLoad() {
super.viewDidLoad()
// AR Session 開始
self.scnView.delegate = self
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
self.scnView.session.run(configuration, options: [.removeExistingAnchors, .resetTracking])
// カーソルノード準備
let pyramid = SCNPyramid(width: 0.1, height: 0.03, length: 0.1)
pyramid.firstMaterial!.diffuse.contents = UIColor.yellow
self.cursorNode.geometry = pyramid
self.scnView.scene.rootNode.addChildNode(self.cursorNode)
self.cursorNode.isHidden = true
}
//
// アンカーが追加された
//
func renderer(_: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// 平面ジオメトリノードを追加
guard let geometory = ARSCNPlaneGeometry(device: self.device) else { return }
geometory.update(from: planeAnchor.geometry)
let material = SCNMaterial()
material.lightingModel = .physicallyBased
material.diffuse.contents = UIColor.red.withAlphaComponent(0.7)
geometory.materials = [material]
let planeNode = SCNNode(geometry: geometory)
DispatchQueue.main.async {
node.addChildNode(planeNode)
}
}
//
// アンカーが更新された
//
func renderer(_: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
DispatchQueue.main.async {
for childNode in node.childNodes {
// 平面ジオメトリを更新
guard let planeGeometry = childNode.geometry as? ARSCNPlaneGeometry else { continue }
planeGeometry.update(from: planeAnchor.geometry)
break
}
}
}
//
// フレームごとに呼び出される
//
func renderer(_ renderer: SCNSceneRenderer, updateAtTime _: TimeInterval) {
DispatchQueue.main.async {
// 画面中央でヒットテスト
let bounds = self.scnView.bounds
let screenCenter = CGPoint(x: bounds.midX, y: bounds.midY)
let results = self.scnView.hitTest(screenCenter, types: [.existingPlaneUsingGeometry])
guard let existingPlaneUsingGeometryResult = results.first(where: { $0.type == .existingPlaneUsingGeometry }),
let _ = existingPlaneUsingGeometryResult.anchor as? ARPlaneAnchor else {
// カーソル非表示
self.cursorNode.isHidden = true
return
}
// ヒットした場所のtransformをカーソルのtransformに転記
let worldTransform = existingPlaneUsingGeometryResult.worldTransform
var cursorTransform = worldTransform
// 平面が向いている方向(UP)に0.01mずらした位置にカーソルを設定
cursorTransform.columns.3 += worldTransform.columns.1 * 0.01
self.cursorNode.simdTransform = cursorTransform
self.cursorNode.isHidden = false
}
}
}
Author And Source
この問題について(ARKit+SceneKitでオブジェクトを配置するときのカーソル表示), 我々は、より多くの情報をここで見つけました https://qiita.com/pic18f14k50/items/69dd645e8352bca744eb著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .