【问题标题】:Place multiple SCN objects in touchesBegan method在 touchesBegan 方法中放置多个 SCN 对象
【发布时间】:2020-02-06 05:36:33
【问题描述】:

下面我的 Swift 代码使用 func touchesBegan 在 ARKit 视图中放置一个 SCN 对象。问题是——它只放置一次对象。我想以一种用户可以选择任何区域来放置 SCN 对象的方式创建代码,并且它也可以根据需要多次放置它。

这里是 GitHub link

class ViewController: UIViewController, ARSCNViewDelegate { 

    @IBOutlet var sceneView: ARSCNView! 

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        // Handle the shooting
        guard let frame = sceneView.session.currentFrame else { return }
        let camMatrix = SCNMatrix4(frame.camera.transform)

        let direction = SCNVector3Make(camMatrix.m31 * 5.0, 
                                       camMatrix.m32 * 10.0, 
                                       camMatrix.m33 * 5.0)

        let position = SCNVector3Make(camMatrix.m41, camMatrix.m42, camMatrix.m43)
        let scene = SCNScene(named: "art.scnassets/dontCare.scn")!
        sceneView.scene = scene
    }    
}  

【问题讨论】:

  • Scene graph 中看不到Area 对象可能有多种原因:1. 3D 几何体的法线反转,2. 纹理的 UV 坐标损坏,3. 纹理损坏文件、4.错误的层次结构、5.零不透明度对象等...首先尝试通过 Xcode Inspector Material 插槽为该对象分配新纹理。但这是另一个问题))),这与touchesBegan 方法无关......

标签: swift scenekit augmented-reality arkit hittest


【解决方案1】:

解决方案 1 – 使用 func touchesBegan(:with:) 添加 3D 模型

使用以下代码获得所需的效果(在场景中放置任意数量的对象):

为了方便起见,首先创建一个扩展:

import ARKit

extension SCNVector3 {
    
    static func + (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 {
        return SCNVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z)
    }
}

然后在您的 ViewController 中使用它来将pointOfView.position 添加到desiredVector

class ViewController: UIViewController {

    @IBOutlet var sceneView: ARSCNView!
    
    override func touchesBegan(_ touches: Set<UITouch>,
                              with event: UIEvent?) {
        
        sceneView.isMultipleTouchEnabled = true
        
        guard let pointOfView = sceneView.pointOfView   // ARCamera of SCNScene 
        else { return }
        
        let cameraMatrix = pointOfView.transform
        
        let desiredVector = SCNVector3(cameraMatrix.m31 * -0.5,
                                       cameraMatrix.m32 * -0.5,
                                       cameraMatrix.m33 * -0.5)
        
        // What the extension SCNVector3 is for //
        let position = pointOfView.position + desiredVector 
        
        let sphereNode = SCNNode()
        sphereNode.geometry = SCNSphere(radius: 0.05)
        sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green
        sphereNode.position = position
        sceneView.scene.rootNode.addChildNode(sphereNode)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let scene = SCNScene(named: "art.scnassets/myScene.scn")!
        sceneView.scene = scene
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let config = ARWorldTrackingConfiguration()
        sceneView.session.run(config)
    }
}

如果您想从 .scn 文件中检索 3D 模型,请使用以下代码

(而不是sphereNode):

var modelNode = SCNNode()
let myScene = SCNScene(named: "art.scnassets/ship.scn")

// Model's name in a Scene graph hierarchy.
// Pay particular attention – it's not a name of .scn file.
let nodeName = "ship"
    
modelNode = (myScene?.rootNode.childNode(withName: nodeName, recursively: true))!
modelNode.position = position
sceneView.scene.rootNode.addChildNode(modelNode)

解决方案 2 - 使用平面检测和命中测试添加 3D 模型

如果您想使用平面检测和命中测试添加模型,请使用以下代码:

为了方便起见,首先创建一个扩展:

extension float4x4 {

    var simdThree: SIMD3<Float> {
        let translation = self.columns.3            
        return SIMD3<Float>(translation.x, translation.y, translation.z)
    }
}

然后在主代码中使用:

class ViewController: UIViewController {

    @IBOutlet weak var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        addGesture()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        sceneView.delegate = self                  // for ARSCNViewDelegate

        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal]
        sceneView.session.run(config)
    }

    func addGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, 
                                                action: #selector(ViewController.addModel))
        sceneView.addGestureRecognizer(tapGesture)
    }

    // Don't forget to drag-and-drop TapGestureRecognizer object from library
    @objc func addModel(recognizer: UIGestureRecognizer) {
        
        let tap: CGPoint = recognizer.location(in: sceneView)

        let results: [ARHitTestResult] = sceneView.hitTest(tap, 
                                                    types: .existingPlaneUsingExtent)
        
        guard let hitTestResult = results.first 
        else { return }

        let translation = hitTestResult.worldTransform.simdThree
        let x = translation.x
        let y = translation.y
        let z = translation.z
        
        guard let scene = SCNScene(named: "art.scnassets/myScene.scn"),
              let robotNode = scene.rootNode.childNode(withName: "robot", 
                                                    recursively: true)
        else { return }

        robotNode.position = SCNVector3(x, y, z)
        robotNode.scale = SCNVector3(0.02, 0.02, 0.02)
        sceneView.scene.rootNode.addChildNode(robotNode)
    }
}

而且,您必须在 ARPlaneAnchors 的两个 renderer() 方法中实现一个逻辑

extension ViewController: ARSCNViewDelegate {

    func renderer(_ renderer: SCNSceneRenderer,
                 didAdd node: SCNNode,
                  for anchor: ARAnchor) { // your logic here....  }

    func renderer(_ renderer: SCNSceneRenderer,
              didUpdate node: SCNNode,
                  for anchor: ARAnchor) { // your logic here....  }

}

【讨论】:

  • 我在使用未解析的标识符“配置”时出现错误
  • 尝试您的代码在屏幕上触摸时没有显示任何内容。用你的版本更新了我上面的链接。感谢您到目前为止的帮助。
  • 代码中的那些方法并没有对它们内部的任何内容进行正确处理
  • 我尝试了选项 1,它有效。它给了我 1 个 scn 对象和添加我想要的所有绿色球体的能力。我只需要知道如何使用绿色球体对 scn 对象执行您正在执行的操作。因此,每次我触摸屏幕而不是绿色球体时,都会出现 scn 对象。感谢您迄今为止的帮助。
  • 我已经用你的代码编辑了我上面的问题。好消息是代码完全按照我想要的方式运行。自定义预加载的 scn 文件。当我尝试添加自己的 scn 文件时。它会导致运行时错误。我也更新了指向我的 GitHub 文件的链接。正是我正在运行的。谢谢。
【解决方案2】:

“对象”是SCNNode。这些显示在 3D 场景中,SCNScene。每次点击屏幕时,都会将场景应用到场景视图,而不是向场景添加节点。

您还需要找到用户在场景中点击的位置,即触摸的 3D 位置。这需要一个命中测试。

试试这个

class ViewController: UIViewController, ARSCNViewDelegate {

@IBOutlet var sceneView: ARSCNView!

override func viewDidLoad() {
    super.viewDidLoad()
    sceneView.delegate = self
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let configuration = ARWorldTrackingConfiguration()
    sceneView.session.run(configuration)
}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard
        let touchPosition = touches.first?.location(in: sceneView),
        let hitTest = sceneView.hitTest(touchPosition, types: .featurePoint).first
        else {return}

    let node = SCNScene(named: "art.scnassets/dontCare.scn")!.rootNode.childNodes.first!
    node.simdTransform = hitTest.worldTransform
    sceneView.scene.rootNode.addChildNode(node)
}

}

【讨论】:

  • 您的代码创建了一个 scnnode 形状并确实将其放置在 ARSCNView 上,就像我想要的那样。我想如何处理我创建的自定义 scn 形状,而不是来自库。感谢您迄今为止的帮助!
  • 您可以从场景中获取节点,我编辑了代码,假设您要查找的节点是您尝试加载的场景。
  • 我尝试了您的代码,但当我触摸时没有任何反应。我通过 GitHub 链接在上面添加了我的代码,这样你就可以准确地看到我在做什么。我不知道我的 ARKit 对象是否太大,但我认为这不是正在发生的事情。感谢您迄今为止的帮助。
  • 从您在 GitHub 中的代码中,“dontCare.scn”文件将相机作为其第一个节点,这就是上面的代码多次添加的内容。所以要修复它,创建一个空节点并将您的 Sphere + Cylinder 作为子节点添加到空节点内。您还需要更改您提到的比例,对象现在是 2x2x2 米。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 2015-06-18
相关资源
最近更新 更多