繁体   English   中英

带有自定义相机的 SceneKit unprojectPoint()

[英]SceneKit unprojectPoint() with custom camera

我正在使用 SceneKit 为 macOS / iPadOS 开发分子可视化工具。 长话短说,我希望当用户在某个 position 处单击(或触摸)屏幕时,会放置一个新原子(在此示例中只是一个 SCNSphere)。

以前,我激活了 SCNView 的 allowCameraControl 属性,这使我可以自由移动相机,并且使用 unprojectPoint() 方法,我可以成功地将新节点放置在触摸位置。 默认相机 controller 的限制是它不能缩放。 当您捏住屏幕时,它会更改相机的 FOV 属性,而不是通过 Z 轴移动它。

因此,我使用 SCNCamera 制作了一个自定义相机节点。 我成功地重新创建了默认的相机行为(移动、旋转),而且我能够正确放大场景。 这样做的缺点是 unprojectPoint() 方法不再像预期的那样工作,因为新节点放置在非常接近相机节点本身的 position 处。 无论我点击场景的哪个位置,那个未投影的点总是非常接近 0、0、10

internal func newNodeAt(point: CGPoint) {
        let pointVector = SCNVector3(point.x, point.y, 0.8)
        let position = self.unprojectPoint(pointVector)
        
        print("x:\(position.x), y: \(position.y), z: \(position.z)")
        
        let newSphere = SCNSphere(radius: 1)
        let newNode = SCNNode(geometry: newSphere)
        
        self.scene?.rootNode.addChildNode(newNode) 
}

摄像机节点设置如下,并直接附加到场景根节点。

    internal func setupCameraNode() -> SCNNode {
        let cam = SCNCamera()
        cam.name = "camera"
        cam.zFar = 200
        cam.zNear = 0.1
        let camNode = SCNNode()
        camNode.camera = cam
        camNode.position = SCNVector3(0, 0, 5)
        camNode.name = "Camera node"
        return camNode
    }

这些是单击场景的随机位置后的打印位置。

x:-0.1988764852285385,  y: -0.05589345842599869, z: 10.920427322387695
x:-0.18989555537700653, y:  0.14564114809036255, z: 10.920427322387695
x: 0.2168566882610321,  y:  0.13085339963436127, z: 10.920427322387695
x: 0.24202580749988556, y: -0.15493911504745483, z: 10.920427322387695
x:-0.06516486406326294, y: -0.1781780868768692,  z: 10.920427322387695
x:-0.08134553581476212, y:  0.12478446960449219, z: 10.920427322387695
x:-0.25866374373435974, y:  0.1456427276134491,  z: 10.920427322387695
x: 0.217658132314682,   y:  0.16270162165164948, z: 10.920427322387695
x: 0.2053154855966568,  y: -0.12679903209209442, z: 10.920427322387695

我想 unprojectPoint() 与观点有关吗? 但我不知道如何解决这个问题。 谢谢。

我认为你在正确的轨道上,你只需要为用户提供某种深度参考。 这是我的类似代码,但是当我调用 airStrike 时,我根据面向用户的平面处理深度,这就是我知道 Z 需要在哪里的方式。

只是没有视觉的猜测,但似乎有几个选择。 在分子中间创建一个参考平面和 ++/-- 以显示水龙头将从深度角度降落的位置。

或者只是让他们把它放在任何地方,然后是 select 它和 depth++/depth——把它放在正确的 position 中。

@objc func handleTap(recognizer: UITapGestureRecognizer)
{
 let location: CGPoint = recognizer.location(in: gameScene)
            
 if(data.isAirStrikeModeOn == true)
 {
  let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
  let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
  gameControl.airStrike(position: scenePoint)
 }
}

经过几天的测试,我找到了一种解决方法,现在我可以将节点正确放置在它们应该在的位置。

我的节点树是这样的:

  • 根节点
    • 相机节点
    • 原子节点
      • 原子(单个球体)

因此,我所要做的就是将未投影的 position 从 RootNode(我想是相机从中获取参考的那个)转换为 atomNodes,因此:

let unprojected = unprojectPoint(SCNVector3(location.x, location.y, 0.99))                                     
let position = atomNodes.convertPosition(unprojected, from: rootNode)

在我看来,0.99 只是一个不错的 Z position 用于放置球体。 (更多信息在这里

我的建议是始终检查节点树,因为位置是相对的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM