ARKit – 使用 raycastQuery 而非 hitTest 来点击节点,这已被弃用

Posted

技术标签:

【中文标题】ARKit – 使用 raycastQuery 而非 hitTest 来点击节点,这已被弃用【英文标题】:ARKit – Tap node with raycastQuery instead of hitTest, which is deprecated 【发布时间】:2021-05-16 13:49:28 【问题描述】:

ios 14 中,hitTest(_:types:) 已被弃用。看来您现在应该使用raycastQuery(from:allowing:alignment:)。来自documentation:

光线投射是在现实环境中寻找表面位置的首选方法,但为了兼容性,命中测试功能仍然存在。通过跟踪光线投射,ARKit 会继续优化结果,以提高您通过光线投射放置的虚拟内容的位置准确性。

但是,我如何通过光线投射来测试SCNNodes?我只看到了测试飞机的选项。

raycastQuery method documentation Only choices for allowing: are planes

这是我当前的代码,它使用命中测试来检测立方体节点上的点击并将其变为蓝色。

class ViewController: UIViewController 

    @IBOutlet weak var sceneView: ARSCNView!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        /// Run the configuration
        let worldTrackingConfiguration = ARWorldTrackingConfiguration()
        sceneView.session.run(worldTrackingConfiguration)
        
        /// Make the red cube
        let cube = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
        cube.materials.first?.diffuse.contents = UIColor.red
        
        let cubeNode = SCNNode(geometry: cube)
        cubeNode.position = SCNVector3(0, 0, -0.2) /// 20 cm in front of the camera
        cubeNode.name = "ColorCube"
        
        /// Add the node to the ARKit scene
        sceneView.scene.rootNode.addChildNode(cubeNode)
    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
        super.touchesBegan(touches, with: event)
        guard let location = touches.first?.location(in: sceneView) else  return 
        
        let results = sceneView.hitTest(location, options: [SCNHitTestOption.searchMode : 1])
        for result in results.filter(  $0.node.name == "ColorCube" )   /// See if the beam hit the cube
            let cubeNode = result.node
            cubeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue /// change to blue
        
    

如何将let results = sceneView.hitTest(location, options: [SCNHitTestOption.searchMode : 1]) 替换为等效的raycastQuery 代码?

【问题讨论】:

【参考方案1】:

关于命中测试

官方文档说,iOS 14 中仅弃用了 ARKit 的 hitTest(_:types:) 实例方法。但是,在 iOS 15 中,您仍然可以使用它。 ARKit 的命中测试方法应该被替换为光线投射方法。

已弃用的命中测试:

let results: [ARHitTestResult] = sceneView.hitTest(sceneView.center, 
                                           types: .existingPlaneUsingGeometry)

Raycasting 等效项:

let raycastQuery: ARRaycastQuery? = sceneView.raycastQuery(
                                                      from: sceneView.center, 
                                                  allowing: .estimatedPlane, 
                                                 alignment: .any)

let results: [ARRaycastResult] = sceneView.session.raycast(raycastQuery!)

如果您更喜欢光线投射方法来击中节点(实体),请使用 RealityKit 模块而不是 SceneKit:

let arView = ARView(frame: .zero)

let query: CollisionCastQueryType = .nearest
let mask: CollisionGroup = .default
    
let raycasts: [CollisionCastHit] = arView.scene.raycast(from: [0, 0, 0], 
                                                          to: [5, 6, 7],  
                                                       query: query, 
                                                        mask: mask, 
                                                  relativeTo: nil)
    
guard let raycast: CollisionCastHit = raycasts.first else  return 
    
print(raycast.entity.name)

附言

无需寻找替代 SceneKit 的 hitTest(_:options:) 实例方法返回 [SCNHitTestResult],因为它工作正常,现在不是制作的时候它已弃用。

【讨论】:

感谢您的快速答复!但是有没有办法从let results: [ARRaycastResult]获取立方体节点? 知道了。所以基本上,如果我仍在使用 ARKit + SceneKit,即使它已被弃用,也只需进行命中测试? sceneView 对象(ARSCNView 类型)有 3 个hitTest() 方法。其中一个已被弃用,返回[ARHitTestResult]——它用于命中测试检测到的飞机。第二个hitTest() 返回UIView?, and the third one returns [SCNHitTestResult]`,这对于针对节点的纯 SceneKit 命中测试很有用。第三种方法没有被弃用——你可以将它用于 AR 应用程序和 VR 应用程序。因此,您在 ARSCNView for iOS 14 中的武器库是 2 个 hitTest 方法和 1 个 raycastQuery() 方法。此外,ARSCNView 的会话对象允许您对检测到的平面应用光线投射方法。 但是,如果您使用的是 ARView(ARKit 也可以使用 RealityKit 的视图),则只能使用光线投射方法 - arView.raycast(...)(返回 [ARRaycastResult]),arView.session.raycast(...)(返回 [ARRaycastResult] ])、arView.session.trackedRaycast(...) 和 2 个方法 arView.scene.raycast(...) hitTestraycast 方法不是来自协议。您可以对 SceneKit 节点使用命中测试,也可以对 RealityKit 实体使用光线投射。对于 ARKit 检测到的飞机,您必须使用 raycasting

以上是关于ARKit – 使用 raycastQuery 而非 hitTest 来点击节点,这已被弃用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Unity ARKit 激活场景中的对象而不是预制件

通过 Swift 设置 ARKit 方向

ARKit - 如何沿识别的表面移动物体?

ARKit 大型模型跟随相机而不是保持静止

ARKit 显示损坏的 UIWebView

ARKit 初探