我在将设备动作(传感器融合)映射到SceneKit节点旋转时遇到了困难。问题的前提如下:
我有一个球体,并且相机被放置在球体内部,使得球体的几何中心和相机节点的中心重合。我想要实现的是当我围绕一个点物理旋转时,运动需要准确地映射到相机上。
实现细节如下:
我有一个节点,其几何形状为球体,并且是根节点的子节点。我正在使用传感器融合获取姿态四元数,然后将它们转换为欧拉角。其代码如下:
- (SCNVector3)eulerAnglesFromCMQuaternion:(CMQuaternion) {
GLKQuaternion gq1 = GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(90), 1, 0, 0);
GLKQuaternion gq2 = GLKQuaternionMake(q.x, q.y, q.z, q.w);
GLKQuaternion qp = GLKQuaternionMultiply(gq1, gq2);
CMQuaternion rq = {.x = qp.x, .y = qp.y, .z = qp.z, .w = qp.w};
CGFloat roll = atan2(-rq.x*rq.z - rq.w*rq.y, .5 - rq.y*rq.y - rq.z*rq.z);
CGFloat pitch = asin(-2*(rq.y*rq.z + rq.w*rq.x));
CGFloat yaw = atan2(rq.x*rq.y - rq.w*rq.z, .5 - rq.x*rq.x - rq.z*rq.z);
return SCNVector3Make(roll, pitch, yaw);
}
转换方程来自《3D Math Primer for Graphics and Game Development》。我用这本书作为参考,但没有读过它。但绝对在我的阅读列表上。
现在要在SceneKit中模拟物理旋转,我正在旋转包含SCNCamera的节点。这是一个子节点,位于rootNode下面。使用.rotation属性进行操作。需要注意的一点是:
现在我的设备动作更新线程看起来像这样
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:mq withHandler:^(CMDeviceMotion *motion, NSError *error) {
CMAttitude *currentAttitude = motion.attitude;
[SCNTransaction begin];
[SCNTransaction setDisableActions:YES];
SCNVector3 v = [self eulerAnglesFromCMQuaternion:currentAttitude.quaternion];
self.cameraNode.eulerAngles = SCNVector3Make( v.y, -v.x, 0); //SCNVector3Make(roll, yaw, pitch)
[SCNTransaction commit];
}];
需要注意的是,设备欧拉角与SceneKit节点的欧拉角是不同的。下面的链接解释了它们之间的区别。
在我的理解中,它们之间的映射关系如下图所示。
现在我面临的问题是,在我固定点旋转一圈之前,相机的 360 度旋转已经完成了。我的情况类似于以下图片。
我怀疑四元数 -> 欧拉角转换导致了错误,而四元数数学知识目前对我来说还过于高深难懂。
我希望我已经提供了所有信息,如果需要更多信息,我很乐意添加。