Swift+SceneKit:基于经纬度调整节点旋转实现Torus贴合地球
解决方案:根据经纬度让圆环精准贴合SceneKit地球模型
核心问题分析
你当前的旋转函数错误引入了Z轴旋转,且旋转角度的对应关系不符合SceneKit坐标系与地理坐标系的映射逻辑,导致无法适配任意经纬度。要让圆环贴合球面,需明确坐标系对应关系和旋转顺序:
- SceneKit采用右手坐标系,Y轴向上(对应地球北极)
- 地理经度是绕Y轴(地轴)的旋转,纬度是绕X轴的旋转
- 圆环需放置在球面对应经纬度点,且平面垂直于该点的半径(法线指向球心)
正确实现步骤
1. 定义地球半径与圆环节点初始设置
假设地球节点中心在原点,先将圆环初始放在赤道0经度位置(X轴正方向):
let earthRadius: CGFloat = 1.0 // 根据你的地球模型调整 let lowerCircle = SCNTorus(ringRadius: 0.1, pipeRadius: 0.02) // 示例圆环参数 let dotNode = SCNNode(geometry: lowerCircle) // 初始位置:赤道0经度点 dotNode.position = SCNVector3(earthRadius, 0, 0)
2. 修正旋转计算函数
只需要X轴(纬度)和Y轴(经度)旋转,无需Z轴旋转。旋转顺序遵循SceneKit的X→Y→Z应用逻辑:
func getRotation(lat: Double, long: Double) -> SCNVector3 { let latRad = lat * .pi / 180.0 let lonRad = long * .pi / 180.0 // 绕X轴转纬度:北纬为正,向上旋转;南纬为负,向下旋转 let rotationX = Float(latRad) // 绕Y轴转经度:东经为正,逆时针旋转(从Y轴正方向俯视) let rotationY = Float(lonRad) return SCNVector3(rotationX, rotationY, 0) } // 应用旋转 dotNode.eulerAngles = getRotation(lat: viewModel.currentLocation?.lat ?? 0.0, long: viewModel.currentLocation?.long ?? 0.0)
3. (可选)用矩阵变换避免万向锁问题
如果需要更稳定的变换(比如处理极端纬度),可以用SCNMatrix4构建变换矩阵,避免欧拉角的万向锁缺陷:
func transformForLocation(lat: Double, long: Double, radius: CGFloat) -> SCNMatrix4 { let latRad = lat * .pi / 180.0 let lonRad = long * .pi / 180.0 // 构建旋转与平移矩阵 let rotateX = SCNMatrix4MakeRotation(Float(latRad), 1, 0, 0) let rotateY = SCNMatrix4MakeRotation(Float(lonRad), 0, 1, 0) let translate = SCNMatrix4MakeTranslation(Float(radius), 0, 0) // 矩阵乘法顺序:从右到左应用(先平移,再X旋转,最后Y旋转) return SCNMatrix4Mult(SCNMatrix4Mult(rotateY, rotateX), translate) } // 应用变换 dotNode.transform = transformForLocation(lat: viewModel.currentLocation?.lat ?? 0.0, long: viewModel.currentLocation?.long ?? 0.0, radius: earthRadius)
验证测试
- 北极点(lat=90, long=0):节点会转到
(0, earthRadius, 0),圆环贴合北极表面 - 赤道东经90度(lat=0, long=90):节点转到
(0, 0, earthRadius),圆环贴合赤道对应点
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

