SceneKit 不包含用于进行四元数数学运算的库。 SCNQuaternion 只是 SCNVector4 的类型别名——换句话说,它是一个存放四元数的四个系数的地方,但你需要自己做数学来解释或使用它们。
在 Xcode 9 / iOS 11 / macOS 10.13 High Sierra / 等中,有一种更好的方法来处理 SceneKit 中的四元数(和其他矢量/矩阵数学):SIMD 库包括两种四元数类型(用于双精度和浮点数), SceneKit 复制所有 SCNVector/SCNMatrix/SCNQuaternion 访问器以提供 SIMD 版本。使用 SIMD 类型而不是 SCN 类型有很多很好的理由:
- SIMD 类型在多个 iOS/macOS/etc 框架中使用,因此您可以使用它们在(例如)SceneKit、ARKit 和 Model I/O 之间传递向量/矩阵/四元数值,而无需转换或转换。
- Metal 着色器语言对其向量/矩阵类型使用相同的库,因此您的数学代码可以在 CPU 和 GPU 之间轻松移植。
- 在 Swift 中,SIMD 类型显示为带有初始化程序、属性和方法的结构,因此您的大多数 (*) 代码都可以对 Swift 友好。
- 在 Swift 和 C++ 中,运算符重载涵盖了所有基本操作,因此您的数学代码和读写就像数学一样。
- 以上所有内容都是使用编译器内部函数实现的,因此它们使用运行代码的 CPU(x86 SSE、ARM NEON)的本机向量指令集来快速运行。
不幸的是,CoreMotion 还没有(还没有?)为其矢量/矩阵/四元数类型采用 SIMD 库,因此在使用 SIMD 函数或 SceneKit SIMD 之前,您需要将输入的 CMQuaternion 转换为 simd_quatf基于 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
}
(*) 一些有用的 SIMD 操作仍然是 C 风格的全局函数:检查 simd/quaternion.h 是否有像 slerp 和 Bézier 插值这样的东西。
对于早期的 Xcode / SDK 版本...
在 Xcode 9 /iOS 11 / macOS 10.13 / 等之前,SIMD 中没有四元数,SceneKit 中也没有 SIMD 特定的访问器。对于除四元数之外的所有内容,采用 SCN 类型,将它们按组件转换为等效的 SIMD 类型,然后在那里进行数学运算仍然很有用。
Apple 确实在 GLKit 中提供了一个四元数库。使用GLKQuaternionMake 从SCNQuaternion 的四个组成部分中创建一个GLKQuaternion。然后你可以乘它,slerp 它,无论使用GLKQuaternion 函数。完成后,使用结果的组件创建另一个SCNQuaternion。
在 Swift 1.x (Xcode 6.x) 中,SIMD 库在 Swift 中不可用。 GLKit 数学是一个不错的替代品,但你最好只使用更新的 Xcode - 请记住,即使使用 Swift 4,你也可以一直定位到 iOS 7 / macOS 10.9 Mavericks(并且 SIMD 库早在iOS 8 / macOS 10.10 优胜美地)。