如何正确使用iOS(Swift)SceneKit SCNSceneRenderer

我正在iOS上使用SceneKit开发一些代码,在我的代码中,我想确定全局z平面上的x和y坐标,其中z为0.0,而x和y由轻击手势确定。我的设置如下:


    override func viewDidLoad() {

    super.viewDidLoad()


    // create a new scene

    let scene = SCNScene()


    // create and add a camera to the scene

    let cameraNode = SCNNode()

    let camera = SCNCamera()

    cameraNode.camera = camera

    scene.rootNode.addChildNode(cameraNode)

    // place the camera

    cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)


    // create and add an ambient light to the scene

    let ambientLightNode = SCNNode()

    ambientLightNode.light = SCNLight()

    ambientLightNode.light.type = SCNLightTypeAmbient

    ambientLightNode.light.color = UIColor.darkGrayColor()

    scene.rootNode.addChildNode(ambientLightNode)


    let triangleNode = SCNNode()

    triangleNode.geometry = defineTriangle();

    scene.rootNode.addChildNode(triangleNode)


    // retrieve the SCNView

    let scnView = self.view as SCNView


    // set the scene to the view

    scnView.scene = scene


    // configure the view

    scnView.backgroundColor = UIColor.blackColor()

    // add a tap gesture recognizer

    let tapGesture = UITapGestureRecognizer(target: self, action: "handleTap:")

    let gestureRecognizers = NSMutableArray()

    gestureRecognizers.addObject(tapGesture)

    scnView.gestureRecognizers = gestureRecognizers

}


如你看到的。我有一个三角形,该三角形的高度为4个单位,宽为4个单位,并设置在以x,y(0.0,0.0)为中心的z平面(z = 0)上。相机是默认的SCNCamera,它沿负z方向看,我将其放置在(0,0,15)处。zNear和zFar的默认值分别为1.0和100.0。在我的handleTap方法中,我获取水龙头的x和y屏幕坐标,并尝试在z = 0.0的情况下查找x和y全局场景坐标。我正在使用unprojectPoint的电话。


unprojectPoint的文档指示


取消投影z坐标为0.0的点将返回近裁剪平面上的点;取消投影z坐标为1.0的点将返回远裁剪平面上的点。


尽管并没有特别说明,对于两者之间的点,近平面和远平面之间存在线性关系,但我做出了这一假设,并将screenZ的值计算为z =的近平面和远平面之间的距离百分比。 0平面位于。要查看答案,我可以单击三角形的角附近,因为我知道它们在全局坐标中的位置。


我的问题是,当我开始更改相机上的zNear和zFar剪切平面时,我没有获得正确的值,也没有获得一致的值。所以我的问题是,我该怎么做?最后,我将创建一个新的几何并将其放置在与用户单击位置相对应的z平面上。


在此先感谢您的帮助。


繁花如伊
浏览 841回答 3
3回答

白衣染霜花

3D图形管线中的典型深度缓冲区不是线性的。透视划分导致归一化设备坐标中的深度处于不同的比例。(另请参见此处。)因此,您要输入的z坐标unprojectPoint实际上并不是您想要的。那么,如何找到与世界空间中的平面匹配的归一化深度坐标呢?好吧,如果该平面与相机正交(这就是您的相机)是有帮助的。然后,您要做的就是在该平面上投影一个点:let projectedOrigin = gameView.projectPoint(SCNVector3Zero)现在,您可以在3D视图+归一化深度空间中找到世界原点的位置。要将2D视图空间中的其他点映射到该平面上,请使用此向量的z坐标:let vp = gestureRecognizer.locationInView(scnView)let vpWithZ = SCNVector3(x: vp.x, y: vp.y, z: projectedOrigin.z)let worldPoint = gameView.unprojectPoint(vpWithZ)这样可以在世界空间中获得一个点,该点将单击/点击位置映射到z = 0平面,position如果要向用户显示该位置,则适合用作节点的位置。(请注意,此方法仅在映射到垂直于相机视线方向的平面上才有效。如果要将视线坐标映射到不同方向的表面上,则in中的归一化深度值vpWithZ将不会恒定)

30秒到达战场

经过一些实验后,我们开发出了将触摸点投影到场景中给定点任意深度的工具。您需要进行的修改是计算Z = 0平面与此线的交点,这就是您的观点。private func touchPointToScenePoint(recognizer: UIGestureRecognizer) -> SCNVector3 {&nbsp; &nbsp; // Get touch point&nbsp; &nbsp; let touchPoint = recognizer.locationInView(sceneView)&nbsp; &nbsp; // Compute near & far points&nbsp; &nbsp; let nearVector = SCNVector3(x: Float(touchPoint.x), y: Float(touchPoint.y), z: 0)&nbsp; &nbsp; let nearScenePoint = sceneView.unprojectPoint(nearVector)&nbsp; &nbsp; let farVector = SCNVector3(x: Float(touchPoint.x), y: Float(touchPoint.y), z: 1)&nbsp; &nbsp; let farScenePoint = sceneView.unprojectPoint(farVector)&nbsp; &nbsp; // Compute view vector&nbsp; &nbsp; let viewVector = SCNVector3(x: Float(farScenePoint.x - nearScenePoint.x), y: Float(farScenePoint.y - nearScenePoint.y), z: Float(farScenePoint.z - nearScenePoint.z))&nbsp; &nbsp; // Normalize view vector&nbsp; &nbsp; let vectorLength = sqrt(viewVector.x*viewVector.x + viewVector.y*viewVector.y + viewVector.z*viewVector.z)&nbsp; &nbsp; let normalizedViewVector = SCNVector3(x: viewVector.x/vectorLength, y: viewVector.y/vectorLength, z: viewVector.z/vectorLength)&nbsp; &nbsp; // Scale normalized vector to find scene point&nbsp; &nbsp; let scale = Float(15)&nbsp; &nbsp; let scenePoint = SCNVector3(x: normalizedViewVector.x*scale, y: normalizedViewVector.y*scale, z: normalizedViewVector.z*scale)&nbsp; &nbsp; print("2D point: \(touchPoint). 3D point: \(nearScenePoint). Far point: \(farScenePoint). scene point: \(scenePoint)")&nbsp; &nbsp; // Return <scenePoint>&nbsp; &nbsp; return scenePoint}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

iOS