как найти центр самолета, найденного в тесте на удар ARKit SceneKit - PullRequest
0 голосов
/ 25 октября 2019

В приложении обнаружения изображений изображение распознается, затем создается непрозрачная плоскость наложения, поэтому, когда пользователь нажимает на экран, тест попадания находит плоскость наложения, и можно создать новый объект. Но я хочу расположить объект точно в центре базового изображения. Как я могу сделать так, чтобы он всегда был в центре изображения / плоскости и имел одинаковую ориентацию. Можно ли это получить по результатам теста на удар? Спасибо за любой совет!

@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
}

1 Ответ

1 голос
/ 26 октября 2019

Если вы уже добавили SCNNode для рендеринга плоскости поверх обнаруженного изображения, то вы можете просто использовать метод hitTest SceneKit, который возвращает узел SceneKit, а не пытаться выполнить тест по геометрии 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()
  }

}

...