SCN四元数乘法

SCNQuaternion Multiplication

我正在尝试使用 Swift 在 SceneKit 中执行 SCNQuaternion 乘法。下面的代码是在 F# (Xamarin iOS Development) 中。我正在尝试将该代码翻译成 Swift.

我卡在了这条线上:让 dqm = SCNQuaternion.Multiply... SceneKit 没有名为 multiply 的成员。

知道我该怎么做吗?

    let rr = CMAttitudeReferenceFrame.XArbitraryCorrectedZVertical
    this.motion.DeviceMotionUpdateInterval <- float (1.0f / 30.0f)
    this.motion.StartDeviceMotionUpdates (rr,NSOperationQueue.CurrentQueue, (fun d e ->
        let a = this.motion.DeviceMotion.Attitude
        let q = a.Quaternion
        let dq = new SCNQuaternion(float32 q.x, float32 q.y, float32 q.z, float32 q.w)
        let dqm = SCNQuaternion.Multiply (dq, SCNQuaternion.FromAxisAngle(SCNVector3.UnitZ, piover2))
        camNode.Orientation <- dqm
        ()
    ))

SceneKit 不包含用于进行四元数数学运算的库。 SCNQuaternion 只是 SCNVector4 的类型别名——换句话说,它是一个保存四元数的四个系数的地方,但你需要自己做数学来解释或使用它们。

在 Xcode 9 / iOS 11 / macOS 10.13 High Sierra 等中,SceneKit 中有一种更好的处理四元数(和其他 vector/matrix 数学)的方法:SIMD库包括两个四元数类型(用于双精度和浮点数),SceneKit 复制所有 SCNVector/SCNMatrix/SCNQuaternion 访问器以提供 SIMD 版本。使用 SIMD 类型而不是 SCN 类型的原因有很多:

  • SIMD 类型用于多个 iOS/macOS/etc 框架,因此您可以使用它们在(例如)SceneKit、ARKit 和模型 I/O 之间传递 vector/matrix/四元数值,而无需强制转换或转换。
  • Metal 着色器语言对其 vector/matrix 类型使用相同的库,因此您的数学代码可以在 CPU 和 GPU 之间轻松移植。
  • 在 Swift 中,SIMD 类型显示为具有初始值设定项、属性和方法的结构,因此您的大部分 (*) 代码可以 Swift 友好。
  • 在Swift和C++中,运算符重载涵盖了所有的基本操作,所以你的数学代码和读写就像数学一样。
  • 以上所有内容都是使用编译器内部函数实现的,因此它们使用 CPU 的本机向量指令集,您的代码 运行 在(x86 SSE,ARM NEON)上运行速度更快。

不幸的是,CoreMotion 还没有(还?)为其 vector/matrix/四元数类型采用 SIMD 库,因此您需要将输入 CMQuaternion 转换为 simd_quatf在使用 SIMD 函数或基于 SceneKit SIMD 的 API 之前。如果您发现自己经常这样做,您可能需要对其进行扩展:

extension simd_quatf {
    init(_ cmq: CMQuaternion) {
        self.init(ix: Float(cmq.x), iy: Float(cmq.y), iz: Float(cmq.z), r: Float(cmq.w))
    }
}

然后(假设我没看错你的 F#),你的代码在 Swift:

中变成这样
let zAxis = float3(x: 0, y: 0, z: 1)
let rotateAroundZ = simd_quatf(angle: .pi/2, axis: zAxis)
manager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: queue) { motion, error in
    guard let motion = motion else { /* handle error and */ return }

    let deviceQuaternion = simd_quatf(motion.attitude.quaternion)
    cameraNode.simdOrientation = deviceQuaternion * rotateAroundZ
}

(*) Some of the useful SIMD operations are still C-style global functions: Check simd/quaternion.h for stuff like slerp and Bézier interpolation.


对于早期 Xcode/SDK 版本...

  • 在 Xcode 9 /iOS 11 / macOS 10.13 / etc 之前,SIMD 中没有四元数,SceneKit 中也没有特定于 SIMD 的访问器。对于除四元数以外的所有内容,采用 SCN 类型、将它们按组件方式转换为等效的 SIMD 类型并在那里进行数学运算仍然很有用。

  • Apple 确实在 GLKit 中提供了一个四元数库。使用 GLKQuaternionMakeSCNQuaternion 的四个组成部分组成 GLKQuaternion。然后你可以使用 GLKQuaternion 函数将它相乘,slerp 它。完成后,使用结果的组件制作另一个 SCNQuaternion.

  • 在 Swift 1.x (Xcode 6.x) 中,SIMD 库在 Swift 中不可用。 GLKit 数学是一个半途而废的替代品,但你最好只使用更新的 Xcode — 记住你可以一直瞄准 iOS 7 / macOS 10.9 Mavericks 即使 Swift 4(并且 SIMD 库可追溯到 iOS 8 / macOS 10.10 Yosemite)。