You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 00:05:32