首页 文章

在 ARKit 1.5 中重置会话时删除文本节点

提问于
浏览
0

我对识别 ARKit 1.5 中的图像进行了苹果的例子的稍微修改,以便每次识别图像时,都会在其前面显示一些文本几何形状。

不幸的是,我注意到,当在 StatusView 视图中按下“重置”按钮时,我的文本几何不会被删除,而只会在新场景中心附近重新定位。

相反,如果按下后退按钮(因为我将主视图控制器嵌入到导航控制器中是为了通过一个按钮而不是如示例中那样自动启动 AR 会话),然后再次启动该会话,那么一切都会正确消失。

分析代码后,我可以确定重置按钮和从 ViewController 来回切换均触发相同的功能,如下所示

func resetTracking() {

    guard let referenceImages = loadImageReferences() else {
        fatalError("Missing expected asset catalog resources.")
    }

    let configuration = ARWorldTrackingConfiguration()
    configuration.detectionImages = referenceImages
    configuration.planeDetection = [.horizontal, .vertical]
    session.run(configuration, options: [.resetTracking, .removeExistingAnchors])

    statusViewController.scheduleMessage("Look around to detect images", inSeconds: 7.5, messageType: .contentPlacement)
}

如前所述,该函数可以在 ViewController 的ViewDidLoad方法中调用

override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.delegate = self
    sceneView.session.delegate = self

    // debug scene to see feature points and world's origin ARSCNDebugOptions.showFeaturePoints,
    self.sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin]

    // Hook up status view controller callback(s).
    statusViewController.restartExperienceHandler = { [unowned self] in
        self.restartExperience()
    }   
}

func restartExperience() {
    guard isRestartAvailable else { return }
    isRestartAvailable = false

    statusViewController.cancelAllScheduledMessages()

    resetTracking()

    // Disable restart for a while in order to give the session time to restart.
    DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
        self.isRestartAvailable = true
    }
}

以及在按 StatusViewController 中的重置按钮时。

@IBAction private func restartExperience(_ sender: UIButton) {
    restartExperienceHandler()
}

var restartExperienceHandler: () -> Void = {}

为了进一步参考,我创建了以下可能的文本

func createBillboardText(text: String, position: SCNNode, width: CGFloat, height: CGFloat){

    let newText = splitString(every: 10, string: text)
    let textGeometry = SCNText(string: newText, extrusionDepth: 1.0)

    textGeometry.firstMaterial!.diffuse.contents = UIColor.red
    let textNode = SCNNode(geometry: textGeometry)

    // Update object's pivot to its center
    // https://stackoverflow.com/questions/44828764/arkit-placing-an-scntext-at-a-particular-point-in-front-of-the-camera
    let (min, max) = textGeometry.boundingBox
    let dx = min.x + 0.5 * (max.x - min.x)
    let dy = min.y + 0.5 * (max.y - min.y)
    let dz = min.z + 0.5 * (max.z - min.z)
    textNode.pivot = SCNMatrix4MakeTranslation(dx, dy, dz)
    textNode.scale = SCNVector3(0.005, 0.005, 0.005)

    let plane = SCNPlane(width: width, height: height)
    let blueMaterial = SCNMaterial()
    blueMaterial.diffuse.contents = UIColor.blue
    blueMaterial.transparency = 0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    plane.firstMaterial = blueMaterial
    let parentNode = SCNNode(geometry: plane) // this node will hold our text node

    let yFreeConstraint = SCNBillboardConstraint()
    yFreeConstraint.freeAxes = [.Y] // optionally
    parentNode.constraints = [yFreeConstraint] // apply the constraint to the parent node

    parentNode.addChildNode(textNode)

    position.addChildNode(parentNode)

    sceneView.autoenablesDefaultLighting = true

}

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)方法中称为self.createBillboardText(text: message, position: node, width: referenceImage.physicalSize.width, height: referenceImage.physicalSize.height )

我还找到了这个线程,但我不知道如何在场景中调用该函数。

2 回答

  • 2

    我相信您要处理删除SCNNodes的方法是在resetFunction中调用以下函数:

    self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in
        existingNode.removeFromParentNode()
    }
    

    augmentedRealityView代表ARSCNView

    您还可以将特定节点存储在[SCNNode]的数组中,然后遍历它们以删除它们 e.g:

    for addedNode in nodesAdded{
    
    addedNode.removeFromParentNode()
    
    }
    

    希望能帮助到你...

  • 1

    如果要删除所有节点,请使用以下命令:

    self.sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
        node.removeFromParentNode()
    }
    

    现在,如果要删除特定节点,请使用以下命令:

    self.sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
            if node.name == "textNode" {
                node.removeFromParentNode()
            }
    }
    

    它们也适用于SCNNode而非ARSCNView,这意味着您可以遍历 childNodes 到SCNNode,而不仅限于场景rootNode

    这是我在项目中使用的代码。在这里,我首先将所有childNodes删除到一个节点,然后删除SCNNode本身。然后我继续暂停会议,这将结束会议。我运行新的会话,最后将 worldOrigin 重置为当前相机位置。如果关闭应用程序的启动角度,则形状可能会获得错误的角度。

    sceneView.scene.rootNode.enumerateHierarchy { (node, stop) in
           node.childNodes.forEach({
              $0.removeFromParentNode()
           })
           node.removeFromParentNode()
    }
    sceneView.session.pause()
    sceneView.session.run(configuration, options: .resetTracking)
    sceneView.session.setWorldOrigin(relativeTransform: matrix)
    

相关问题