如何在SceneKit中根据用户点击位置渲染球体?
SceneKit点击屏幕渲染圆点位置错误解决方案
问题根源
你调用unprojectPoint()时传入的第三个参数错误:该参数需要是0~1之间的归一化深度值(0对应相机近裁剪面,1对应远裁剪面),你直接传入了世界空间的z值5,导致计算出的位置完全偏离预期。
修正方案
方案一:正确使用unprojectPoint计算目标位置
通过计算近、远裁剪面的对应点,插值得到你想要的深度位置:
func handleTap(_ gestureRecognize: UIGestureRecognizer) { let scnView = self.view as! SCNView // 创建红色圆点节点 let sphereGeo = SCNSphere(radius: 0.1) sphereGeo.firstMaterial?.diffuse.contents = UIColor.red let dotNode = SCNNode(geometry: sphereGeo) let tapPos = gestureRecognize.location(in: scnView) // 计算近、远裁剪面上的投影点 let nearWorldPoint = scnView.unprojectPoint(SCNVector3(tapPos.x, tapPos.y, 0)) let farWorldPoint = scnView.unprojectPoint(SCNVector3(tapPos.x, tapPos.y, 1)) // 设定目标深度(相机在z=15,前方10单位处为z=5) let targetZ: Float = 5 // 插值计算目标位置 let depthRatio = (targetZ - nearWorldPoint.z) / (farWorldPoint.z - nearWorldPoint.z) let targetPosition = SCNVector3( nearWorldPoint.x + depthRatio * (farWorldPoint.x - nearWorldPoint.x), nearWorldPoint.y + depthRatio * (farWorldPoint.y - nearWorldPoint.y), targetZ ) dotNode.position = targetPosition scnView.scene?.rootNode.addChildNode(dotNode) }
方案二:使用射线检测(更可靠,支持相机旋转/缩放)
射线检测是SceneKit获取点击对应3D空间点的标准方式,能自动适配相机变换:
func handleTap(_ gestureRecognize: UIGestureRecognizer) { let scnView = self.view as! SCNView // 创建红色圆点节点 let sphereGeo = SCNSphere(radius: 0.1) sphereGeo.firstMaterial?.diffuse.contents = UIColor.red let dotNode = SCNNode(geometry: sphereGeo) let tapPos = gestureRecognize.location(in: scnView) // 执行点击射线检测 let hitResults = scnView.hitTest(tapPos, options: [ SCNHitTestOption.searchMode: SCNHitTestSearchMode.all.rawValue ]) if let hitResult = hitResults.first { // 点击到场景物体时,在物体表面生成圆点 dotNode.position = hitResult.worldCoordinates } else { // 未点击到物体时,在相机前方10单位处生成圆点(对应世界z=5) guard let cameraNode = scnView.pointOfView else { return } let targetPosition = cameraNode.position - cameraNode.worldFront * 10 dotNode.position = targetPosition } scnView.scene?.rootNode.addChildNode(dotNode) }
内容的提问来源于stack exchange,提问作者Rudy Huynh
相关产品推荐
相关产品推荐

