在 ARKit 1.5 中重置会话时删除文本节点
Remove text node when resetting the session in ARKit 1.5
我稍微修改了 Apple's example 关于在 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
}
}
并且在状态下按下重置按钮ViewController。
@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
//
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 )
我也找到了但是我不知道如何在场景中调用那个函数
我相信您想要处理删除 SCNNodes
的方法是在 resetFunction
中调用以下函数:
self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in
existingNode.removeFromParentNode()
}
其中 augmentedRealityView
指的是 ARSCNView
.
您还可以将特定节点存储在 [SCNNode]
数组中,然后遍历它们以删除它们,例如:
for addedNode in nodesAdded{
addedNode.removeFromParentNode()
}
希望对您有所帮助...
如果要删除所有节点,请使用:
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
,这意味着您可以通过子节点循环到 SCNNode
而不仅仅是从场景 rootNode
这是我在项目中使用的代码。在这里,我首先将所有 childNodes
删除到一个节点,然后删除 SCNNode
本身。然后我继续暂停会话,这将结束它。我 运行 新会话并最终将 worldOrigin 重置为当前相机位置。如果应用程序的启动角度关闭,形状可能会出现错误的角度。
sceneView.scene.rootNode.enumerateHierarchy { (node, stop) in
node.childNodes.forEach({
[=12=].removeFromParentNode()
})
node.removeFromParentNode()
}
sceneView.session.pause()
sceneView.session.run(configuration, options: .resetTracking)
sceneView.session.setWorldOrigin(relativeTransform: matrix)
我稍微修改了 Apple's example 关于在 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
}
}
并且在状态下按下重置按钮ViewController。
@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
//
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 )
我也找到了
我相信您想要处理删除 SCNNodes
的方法是在 resetFunction
中调用以下函数:
self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in
existingNode.removeFromParentNode()
}
其中 augmentedRealityView
指的是 ARSCNView
.
您还可以将特定节点存储在 [SCNNode]
数组中,然后遍历它们以删除它们,例如:
for addedNode in nodesAdded{
addedNode.removeFromParentNode()
}
希望对您有所帮助...
如果要删除所有节点,请使用:
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
,这意味着您可以通过子节点循环到 SCNNode
而不仅仅是从场景 rootNode
这是我在项目中使用的代码。在这里,我首先将所有 childNodes
删除到一个节点,然后删除 SCNNode
本身。然后我继续暂停会话,这将结束它。我 运行 新会话并最终将 worldOrigin 重置为当前相机位置。如果应用程序的启动角度关闭,形状可能会出现错误的角度。
sceneView.scene.rootNode.enumerateHierarchy { (node, stop) in
node.childNodes.forEach({
[=12=].removeFromParentNode()
})
node.removeFromParentNode()
}
sceneView.session.pause()
sceneView.session.run(configuration, options: .resetTracking)
sceneView.session.setWorldOrigin(relativeTransform: matrix)