我认为我理解了你的问题,但是你对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 {
let quaternion = GLKQuaternion(angle: Float(rotation.rotation), axis: rotationAxis)
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!)
为了实现加性旋转,我使用四元数而不是向量旋转。基本上,我通过四元数乘法取手势开始时箱子的朝向并施加一个旋转。这两行代码:
let quaternion = GLKQuaternion(angle: Float(rotation.rotation), axis: rotationAxis)
node.orientation = SCNQuaternion((startingOrientation * quaternion).normalized())
这个数学问题也可以使用旋转向量或变换矩阵来解决,但这是我熟悉的方法。
结果
![输入图像描述](https://istack.dev59.com/biWZ5.gif)
扩展
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)
}
}
.began
时反向旋转盒子吗? - Alok Subedi