【问题标题】:How to place 3D object (.scn file) on detected vertical plane which should be parallel to plane in ARKIt?如何将 3D 对象(.scn 文件)放置在检测到的垂直平面上,该平面应该与 ARKIt 中的平面平行?
【发布时间】:2019-05-24 05:16:05
【问题描述】:

我将在 ARKit 中检测水平和垂直平面。检测到是水平面还是垂直面后,分别在检测面上添加灰色平面。在检测到的平面上点击我将添加.scn文件的3D对象。

我的代码在将 3D 对象(.scn 文件)放置在水平面上工作正常,但在垂直面上不能正常工作。

像相框这样的垂直平面的 3D 对象(.scn 文件)在 SceneKit 编辑器中面向右侧。所以我将它的 EularAngleY 更改为 -0,它现在在 SceneKit 编辑器中面向前方。当我点击检测到的面向前方的垂直平面时,相框也向右,如果我将设备向右移动并放置相框,那么它是正确的。

我想放置应该与平面平行的 3D 对象 .scn 文件(如果垂直平面朝前,那么即使在 .scn 文件中它面向任何方向,它也应该朝前)。那个 3D 对象不平行于检测到的平面。

我是否需要根据检测到的平面角度更改旋转,或者需要在 SceneKit 编辑器中对 .scn 文件进行任何更改?我怎样才能实现它?

请检查以下代码以检测到垂直平面。有什么问题吗?

@objc func addObjectToSceneView1(withGestureRecognizer recognizer: UIGestureRecognizer){
    let tapLocation = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)

    guard let hitTestResult = hitTestResults.first, let anchor = hitTestResult.anchor as? ARPlaneAnchor else { return }


    let translation = hitTestResult.worldTransform.columns.3

    let x = translation.x
    let y = translation.y
    let z = translation.z

    guard let shipScene = SCNScene(named: "art.scnassets/frame/frame.scn"),
        let shipNode = shipScene.rootNode.childNode(withName: "frame", recursively: true)
        else { return }

    shipNode.position = SCNVector3(x,y,z)

    sceneView.scene.rootNode.addChildNode(shipNode)
}

.scn 文件如下所示。这个 .scn 文件是否正确?或者 x 应该是框架的深度?每次我点击飞机时,它只会显示这样的图像。

【问题讨论】:

    标签: ios swift 3d arkit


    【解决方案1】:

    正如我在答案here 中提到的,最好在ARSession 中添加ARAnchor,而不是在进行命中测试后直接将SCNNode 添加到场景图中。目前您发布的代码没有考虑到检测到的平面的旋转。为了使代码正常工作,您需要确定检测到的平面的法线,将点积和叉积与模型的所需方向计算旋转,然后应用旋转。但是,ARSession 将为您完成所有这些工作。通过使用ARAnchor(transform: hitTestResult.worldTransform),旋转被编码到锚中。因此,您只需要使用模型自己的局部坐标空间来处理转换。

    例如:

    @objc func addObjectToSceneView1(withGestureRecognizer recognizer: UIGestureRecognizer){
        let tapLocation = recognizer.location(in: sceneView)
        let hitTestResults = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
    
        guard let hitTestResult = hitTestResults.first, let anchor = hitTestResult.anchor as? ARPlaneAnchor else { return }
        // create anchor and add to session and wait for callback
        let anchor = ARAnchor(transform: hitTestResult.worldTransform)
        sceneView.session.add(anchor: anchor)
    }
    

    然后在你的会话委托中回调:

    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        if anchor is ARPlaneAnchor {
            // node for plane anchor
            let anchorNode = SCNNode()
            return anchorNode
        } else {
            // must be node for most recent hit test
            guard let frameScene = SCNScene(named: "art.scnassets/frame/frame.scn"),
                  let frameNode = frameScene.rootNode.childNode(withName: "frame", recursively: true) else { return nil }
            return frameNode
        }
    }
    

    在您的 scn 文件中,您会希望模型位于平放的原点而不进行任何变换。这意味着您可能需要嵌套节点并相对于父空节点定位底层模型。

    这里的“frame”是从nodeForAnchor返回的没有变换的外部节点,“picture”被旋转为平面并缩放到内容的大小。

    最终结果:

    【讨论】:

    • 你好 beyowulf,我已经添加了 .scn 文件。你能检查一下,让我知道所有方向和轴都可以吗?我的意思是x轴是宽度,z轴是深度。当我运行这个项目时,它的显示完全一样。
    • 你能分享这个GIF的完整项目吗? @beyowulf
    猜你喜欢
    • 2019-10-08
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2018-02-09
    • 1970-01-01
    相关资源
    最近更新 更多