首页 > 解决方案 > 如何在命中测试 ARKit SceneKit 中找到飞机的中心

问题描述

在图像检测应用程序中,图像被识别,然后创建一个不透明的覆盖平面,因此当用户在屏幕上点击时,命中测试会找到覆盖平面,并且可以创建一个新对象。但我想将对象准确定位在底层图像的中心。我怎样才能让它始终位于图像/平面的中心,并具有相同的方向。这可以从命中测试结果中得到吗?感谢您的任何建议!

@objc func handleScreenTap(sender: UITapGestureRecognizer) {
  let tappedSceneView = sender.view as! ARSCNView
  let tapLocation = sender.location(in: tappedSceneView)

  let planeIntersections = tappedSceneView.hitTest(tapLocation, types: [.estimatedHorizontalPlane, .estimatedVerticalPlane])
  if !planeIntersections.isEmpty {
    addSceneAtPositionOnPlane(hitTestResult: planeIntersections.first!)
}

func addSceneAtPositionOnPlane(hitTestResult: ARHitTestResult) {

  let transform = hitTestResult.worldTransform
  let positionColumn = transform.columns.3
  let initialPosition = SCNVector3(positionColumn.x,
                                 positionColumn.y,
                                 positionColumn.z)

   let node = self.createScene(for: initialPosition)
   sceneView.scene.rootNode.addChildNode(node)
}

func createScene(for position: SCNVector3) -> SCNNode {

  let box = SCNNode(geometry: SCNBox(width: 0.1, //x
                                    height: 0.1, //y
                                    length: 0.1, //z
                                    chamferRadius: 0))

  box.geometry?.firstMaterial?.diffuse.contents = UIColor.red
  box.geometry?.firstMaterial?.isDoubleSided = true
  box.opacity = 0.8

  box.position = position        
  return box
}

标签: swiftscenekitarkit

解决方案


如果您已经添加了一个 SCNNode 来在检测到的图像之上渲染一个平面,那么您可以只使用返回 SceneKit 节点的 SceneKit hitTest 方法,而不是尝试对 ARKit 几何体进行命中测试。

一旦您将平面添加到场景中,您就可以将新几何图形添加为该节点的子节点。

这是一个示例,一旦检测到图像,就会在其顶部绘制一个平面,然后当用户单击该平面时,会添加一个框作为子项,然后该框将跟随跟踪的图像并具有正确的位置和方向。

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

  @IBOutlet var sceneView: ARSCNView!

  override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.delegate = self

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTap))
    sceneView.addGestureRecognizer(tapGesture)
  }

  @objc func onTap(_ recognizer: UITapGestureRecognizer) {
    let point = recognizer.location(in: sceneView)

    guard let hit = sceneView.hitTest(point, options: nil).first else {
      return
    }

    let box = SCNBox(width: 0.02, height: 0.02, length: 0.02, chamferRadius: 0)
    let node = SCNNode(geometry: box)
    node.position = SCNVector3(0, 0, 0.01)
    box.materials.first?.diffuse.contents = UIColor.red
    hit.node.addChildNode(node)
  }

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let configuration = ARWorldTrackingConfiguration()

    guard let images = ARReferenceImage.referenceImages(inGroupNamed: "ARTest", bundle: nil) else {
      return
    }

    configuration.detectionImages = images
    configuration.maximumNumberOfTrackedImages = 1
    sceneView.session.run(configuration)
  }

  func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let imageAnchor = anchor as? ARImageAnchor else {
      return
    }

    let size = imageAnchor.referenceImage.physicalSize
    let plane = SCNPlane(width: size.width, height: size.height)
    let planeNode = SCNNode(geometry: plane)
    planeNode.eulerAngles.x = -Float.pi / 2
    planeNode.opacity = 0.9
    node.addChildNode(planeNode)
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    sceneView.session.pause()
  }

}


推荐阅读