我想我理解你的问题,但你对 Xartec 的回答的评论让我有点困惑,不知道我是否真的理解。
重申:
目标是围绕通过从相机原点画一条线“直接穿过”对象而形成的矢量旋转对象。这是一个垂直于相机平面的矢量,在这种情况下是手机屏幕。这个向量是相机的-Z轴。
解决方案
根据我对您的目标的理解,这就是您所需要的
private var startingOrientation = GLKQuaternion.identity
private var rotationAxis = GLKVector3Make(0, 0, 0)
@objc private func handleRotation(_ rotation: UIRotationGestureRecognizer) {
guard let node = sceneView.hitTest(rotation.location(in: sceneView), options: nil).first?.node else {
return
}
if rotation.state == .began {
startingOrientation = GLKQuaternion(boxNode.orientation)
let cameraLookingDirection = sceneView.pointOfView!.parentFront
let cameraLookingDirectionInTargetNodesReference = boxNode.convertVector(cameraLookingDirection,
from: sceneView.pointOfView!.parent!)
rotationAxis = GLKVector3(cameraLookingDirectionInTargetNodesReference)
} else if rotation.state == .ended {
startingOrientation = GLKQuaternionIdentity
rotationAxis = GLKVector3Make(0, 0, 0)
} else if rotation.state == .changed {
// This will be the total rotation to apply to the starting orientation
let quaternion = GLKQuaternion(angle: Float(rotation.rotation), axis: rotationAxis)
// Apply the rotation
node.orientation = SCNQuaternion((startingOrientation * quaternion).normalized())
}
}
说明
真正关键的部分是确定要旋转的矢量,幸运的是 SceneKit 提供了非常方便的方法。不幸的是,它们没有提供您需要的所有方法。
首先,您需要表示相机正面的矢量(相机总是朝向其前轴)。 SCNNode.localFront 是 -Z 轴 (0, 0, -1),这只是 SceneKit 中的一个约定。但是您需要在相机父坐标系中代表 Z 轴的轴。我发现我经常需要这个,所以我创建了一个扩展来从 SCNNode 获取 parentFront。
现在我们有了相机的前轴
let cameraLookingDirection = sceneView.pointOfView!.parentFront
要将其转换为目标的参考系,我们使用convertVector(_,from:) 来获得一个可以应用旋转的向量。此方法的结果将是场景首次启动时框的 -Z 轴(就像在您的静态代码中一样,但您使用了 Z 轴并否定了角度)。
let cameraLookingDirectionInTargetNodesReference = boxNode.convertVector(cameraLookingDirection, from: sceneView.pointOfView!.parent!)
为了实现加法旋转,我不清楚你是否需要这部分,我使用四元数而不是矢量旋转。基本上,当手势开始时,我采用框的orientation,并通过四元数乘法应用旋转。这两行:
let quaternion = GLKQuaternion(angle: Float(rotation.rotation), axis: rotationAxis)
node.orientation = SCNQuaternion((startingOrientation * quaternion).normalized())
这个数学也可以用旋转向量或变换矩阵来完成,但这是我熟悉的方法。
结果
扩展
extension SCNNode {
/// The local unit Y axis (0, 1, 0) in parent space.
var parentUp: SCNVector3 {
let transform = self.transform
return SCNVector3(transform.m21, transform.m22, transform.m23)
}
/// The local unit X axis (1, 0, 0) in parent space.
var parentRight: SCNVector3 {
let transform = self.transform
return SCNVector3(transform.m11, transform.m12, transform.m13)
}
/// The local unit -Z axis (0, 0, -1) in parent space.
var parentFront: SCNVector3 {
let transform = self.transform
return SCNVector3(-transform.m31, -transform.m32, -transform.m33)
}
}
extension GLKQuaternion {
init(vector: GLKVector3, scalar: Float) {
let glkVector = GLKVector3Make(vector.x, vector.y, vector.z)
self = GLKQuaternionMakeWithVector3(glkVector, scalar)
}
init(angle: Float, axis: GLKVector3) {
self = GLKQuaternionMakeWithAngleAndAxis(angle, axis.x, axis.y, axis.z)
}
func normalized() -> GLKQuaternion {
return GLKQuaternionNormalize(self)
}
static var identity: GLKQuaternion {
return GLKQuaternionIdentity
}
}
func * (left: GLKQuaternion, right: GLKQuaternion) -> GLKQuaternion {
return GLKQuaternionMultiply(left, right)
}
extension SCNQuaternion {
init(_ quaternion: GLKQuaternion) {
self = SCNVector4(quaternion.x, quaternion.y, quaternion.z, quaternion.w)
}
}
extension GLKQuaternion {
init(_ quaternion: SCNQuaternion) {
self = GLKQuaternionMake(quaternion.x, quaternion.y, quaternion.z, quaternion.w)
}
}
extension GLKVector3 {
init(_ vector: SCNVector3) {
self = SCNVector3ToGLKVector3(vector)
}
}