SceneKit相机角度适配矩阵变换:平移手势3D节点漂移问题
解决SceneKit相机旋转时UIPan手势移动节点的漂移问题
我太懂你这个痛点了——相机垂直平面时平移节点好好的,一旋转相机,触摸位置直接飘得没边儿!这是因为原代码里的unprojectPoint用法没考虑相机视角变化后,触摸射线和目标平面的正确交点逻辑。下面给你修正后的实现,用射线平面相交+向量运算的方法彻底解决漂移问题:
@objc func handlePan(_ sender: UIPanGestureRecognizer) { guard let sceneView = self.sceneView else { return } // 把平移偏移转换成视图内的当前触摸位置 let translation = sender.translation(in: sceneView) let currentTouchPoint = CGPoint( x: sceneView.bounds.midX + translation.x, y: sceneView.bounds.midY + translation.y ) // 生成相机发出的、穿过触摸点的射线(近平面到远平面的两点) let rayStart = sceneView.unprojectPoint(SCNVector3(currentTouchPoint.x, currentTouchPoint.y, 0)) let rayEnd = sceneView.unprojectPoint(SCNVector3(currentTouchPoint.x, currentTouchPoint.y, 1)) // 计算射线的方向向量并归一化 let rayDirection = rayEnd - rayStart let normalizedDirection = rayDirection.normalized() // 定义你要移动节点的目标平面:这里是y=0平面 let planeNormal = SCNVector3(0, 1, 0) // 平面的法向量 let planeOrigin = SCNVector3Zero // 平面上的任意参考点 // 用射线平面相交公式计算交点 let denominator = normalizedDirection.dot(planeNormal) guard abs(denominator) > 0.0001 else { // 射线和平面平行,没交点,直接返回 return } let t = (planeOrigin - rayStart).dot(planeNormal) / denominator let targetWorldPoint = rayStart + normalizedDirection * t // 给节点设置位置(固定y=0) worldHandle?.position = SCNVector3(targetWorldPoint.x, 0, targetWorldPoint.z) } // 给SCNVector3加些辅助计算的扩展,方便向量操作 extension SCNVector3 { static func - (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 { return SCNVector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) } static func + (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 { return SCNVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z) } static func * (lhs: SCNVector3, scalar: Float) -> SCNVector3 { return SCNVector3(lhs.x * scalar, lhs.y * scalar, lhs.z * scalar) } func normalized() -> SCNVector3 { let length = sqrt(x*x + y*y + z*z) guard length > 0.0001 else { return self } return SCNVector3(x/length, y/length, z/length) } func dot(_ vector: SCNVector3) -> Float { return x*vector.x + y*vector.y + z*vector.z } }
为啥这个方法能搞定漂移?
原代码的问题在于,直接用固定z值的unprojectPoint计算位置,当相机旋转后,这个z值对应的空间点根本不在你要的y=0平面上,自然会飘。而修正后的逻辑是:
- 先算出触摸点对应的相机射线(从相机近平面穿到远平面的一条3D线)
- 用几何公式精确计算这条射线和y=0平面的交点——这个交点就是触摸在目标平面上的真实位置
- 不管相机怎么旋转,射线和平面的交点都是基于3D空间的几何关系计算的,完全不受透视偏移影响
小细节调整
如果你的目标平面不是y=0,只需要修改planeNormal(平面法向量)和planeOrigin(平面上任意一点)就行,比如要在y=2的平面上移动节点,把planeOrigin改成SCNVector3(0,2,0)就好。
内容的提问来源于stack exchange,提问作者JP Silvashy
相关产品推荐
相关产品推荐

