我有一个用 swift 和 SceneKit 构建的 3d 地球仪,为了表示用户当前位置,我使用了一个圆环(甜甜圈形状的对象)。用户位置可以是世界上的任何地方,并且简单地将节点添加到地球并不会使其齐平(与地球对齐)。我必须修改节点 eulerAngles 才能使其与地球持平。在给定任何独特位置的情况下,我的功能无法将形状与地球正确对齐。我希望该函数根据任意一组纬度和经度来定位圆环齐平。
let dotNode = SCNNode(geometry: lowerCircle)
dotNode.eulerAngles = getRotation(lat: viewModel.currentLocation?.lat ?? 0.0, long: viewModel.currentLocation?.long ?? 0.0)
func getRotation(lat: Double, long: Double) -> SCNVector3 {
let latRad = lat * .pi / 180.0
let lonRad = long * .pi / 180.0
let rotationX = Float(-latRad)
let rotationY = Float(lonRad)
let rotationZ = Float(lonRad)
return SCNVector3Make(rotationX, rotationY, rotationZ)
//example
//slightly correct rotation for Saudi Arabia
//SCNVector3Make(.pi / 3.75, 0, -.pi / 4)
}
A torus,在 3D 图形或建模的上下文中(例如在 Swift 中使用的 SceneKit 框架中,带有
SCNTorus
),是类似于甜甜圈或戒指的几何形状。
在您的例子中,环面用于表示地球上的特定位置,可能是用户的当前位置。
由于地球是一个球体,仅将环面放置在基于纬度和经度的点上是不够的。环面还需要定向,使其与地球表面齐平。这需要调整其旋转以与该特定点的地球曲率正确对齐。
环面是地球节点的子节点。
圆环是地球节点的子节点,应用于圆环的任何旋转或定位都是相对于地球节点的。这简化了将环面与特定地理位置的地球表面对齐的过程。您无需考虑整个场景的坐标系,只需相对于地球节点调整环面即可。
那就是:
func getTorusOrientation(lat: Double, long: Double) -> SCNVector4 {
let latRad = lat * .pi / 180.0
let lonRad = long * .pi / 180.0
// Calculate position vector
let x = cos(latRad) * cos(lonRad)
let y = cos(latRad) * sin(lonRad)
let z = sin(latRad)
let positionVector = SCNVector3(x, y, z)
// Updated up vector (assuming Y-axis is 'up' in the coordinate system)
let upVector = SCNVector3(0, 1, 0)
// Calculate cross-product
let crossProduct = positionVector.cross(upVector)
// Calculate angle for rotation
let angle = acos(positionVector.dot(upVector))
return SCNVector4(crossProduct.x, crossProduct.y, crossProduct.z, angle)
}
extension SCNVector3 {
func cross(_ vector: SCNVector3) -> SCNVector3 {
return SCNVector3(
self.y * vector.z - self.z * vector.y,
self.z * vector.x - self.x * vector.z,
self.x * vector.y - self.y * vector.x
)
}
func dot(_ vector: SCNVector3) -> Float {
return (self.x * vector.x + self.y * vector.y + self.z * vector.z)
}
}
// Usage
dotNode.orientation = getTorusOrientation(lat: viewModel.currentLocation?.lat ?? 0.0,
long: viewModel.currentLocation?.long ?? 0.0)
positionVector
使用纬度 (lat
) 和经度 (long
) 值计算。该矢量从地球中心(假定为该坐标系中的原点)指向地球表面上环面应放置的位置。
upVector
设置为 (0, 1, 0)
,这是在许多 3D 坐标系中定义“向上”方向的标准方法。
计算 crossProduct
和
positionVector
的
upVector
:它给出了我们需要围绕其旋转环面的轴。该轴垂直于环面的位置向量和初始向上向量,这正是正确方向所需的。旋转的
angle
使用
positionVector
和
upVector
之间的点积计算。该角度是圆环需要绕
crossProduct
轴旋转才能与指定地理位置的地球表面正确对齐的量。从
SCNVector4
轴创建的
crossProduct
和
angle
然后用于设置环面的方向。这确保环面不仅位于正确的纬度和经度,而且方向正确,使其看起来与地球表面齐平。
SCNTorus
可以重用上面定义的
getTorusOrientation()
。
// Create a torus
let torus = SCNTorus(ringRadius: 1.0, pipeRadius: 0.1)
let torusNode = SCNNode(geometry: torus)
// Set the torus orientation (using the previously discussed getTorusOrientation function)
torusNode.orientation = getTorusOrientation(lat: viewModel.currentLocation?.lat ?? 0.0,
long: viewModel.currentLocation?.long ?? 0.0)
// Add the torus node to the earth node
earthNode.addChildNode(torusNode)
您将创建一个具有特定半径值的 SCNTorus
对象,然后将其包装在
SCNNode
中(即
torusNode
)。该节点的方向是使用
getTorusOrientation
函数设置的,使其在地球上正确对齐。最后,将环面节点作为子节点添加到地球节点,以维护正确定位和方向所需的层次关系。