在哪些情况下 ARSCNView.raycastQuery 返回 nil?

rya*_*chi 5 augmented-reality hittest scenekit swift arkit

在我的渲染器委托中,我从视图的中心创建了一个光线投射查询来跟踪估计平面并显示一个跟随光线投射结果的 3D 指针。

它是通过完成的,view.raycastQuery(from:allowing:alignment:)但返回零。

我的问题是为什么?没有文档说明为什么这个函数会返回一个 nil 值。我知道光线投射结果可能为空,但为什么不会创建查询?

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    guard view.session.currentFrame?.camera.trackingState == .normal else {
        return
    }
    DispatchQueue.main.async {
        let center = view.center
        DispatchQueue.global(qos: .userInitiated).async {
            if let query = view.raycastQuery(from: center, allowing: .estimatedPlane, alignment: .any) {
                let results = view.session.raycast(query)
                ...
            }
            else {
                // sometimes it gets here
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ARG*_*Geo 4

返回类型ARRaycastQuery?最初是可选的

根据苹果文档

raycastQuery(from:allowing:alignment:)实例方法创建一条从参数屏幕空间点沿 z 轴正方向延伸的射线,以确定沿该射线的物理环境中是否存在任何参数目标。如果是这样,ARKit 将返回光线与目标相交的 3D 位置。

如果光线不与目标相交,则不存在任何ARRaycastQuery物体。所以有nil.

func raycastQuery(from point: CGPoint, 
             allowing target: ARRaycastQuery.Target, 
                   alignment: ARRaycastQuery.TargetAlignment) -> ARRaycastQuery?
Run Code Online (Sandbox Code Playgroud)

回国的两个可能原因是什么nil

  • 没有检测到射线击中的平面
  • 代码逻辑错误

解决方案

您的代码可能如下所示:

@IBOutlet var sceneView: ARSCNView!

@IBAction func onTap(_ sender: UIGestureRecognizer) {

    let tapLocation: CGPoint = sender.location(in: sceneView)
    let estimatedPlane: ARRaycastQuery.Target = .estimatedPlane      
    let alignment: ARRaycastQuery.TargetAlignment = .any

    let query: ARRaycastQuery? = sceneView.raycastQuery(from: tapLocation,
                                                    allowing: estimatedPlane,
                                                   alignment: alignment)
    
    if let nonOptQuery: ARRaycastQuery = query {

        let result: [ARRaycastResult] = sceneView.session.raycast(nonOptQuery)
        
        guard let rayCast: ARRaycastResult = result.first
        else { return }

        self.loadGeometry(rayCast)
    }
}
Run Code Online (Sandbox Code Playgroud)

这是获取模型的方法:

func loadGeometry(_ result: ARRaycastResult) {

    let scene = SCNScene(named: "art.scnassets/myScene.scn")!

    let node: SCNNode? = scene.rootNode.childNode(withName: "model", 
                                               recursively: true)

    node?.position = SCNVector3(result.worldTransform.columns.3.x,
                                result.worldTransform.columns.3.y,
                                result.worldTransform.columns.3.z)

    self.sceneView.scene.rootNode.addChildNode(node!)
}
Run Code Online (Sandbox Code Playgroud)