首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >SceneKit -映射物理360旋转

SceneKit -映射物理360旋转
EN

Stack Overflow用户
提问于 2014-08-13 17:21:12
回答 2查看 6.2K关注 0票数 18

我很难将设备运动(传感器融合)映射到SceneKit节点旋转。问题的前提是:

我有球体,并且相机被放置在球体内部,使得球体的几何中心和相机节点的中心重合。我想要实现的是,当我围绕一个点进行物理旋转时,运动也需要准确地映射到相机上。

具体实现如下:

我有一个节点,它有一个球体作为几何模型,是根节点的子节点。我正在使用传感器融合来获得姿态四元数,然后将它们转换为欧拉角。它的代码是:

代码语言:javascript
复制
- (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属性来做同样的事情。这里需要注意的一件事是,现在我的设备运动更新线程看起来像这样

代码语言:javascript
复制
[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节点的欧拉角是不同的。这些链接对它们进行了解释。

SceneKit Euler Angles

CMAttitude Euler Angles

在我的理解中,两者之间的映射是这样的。

现在我面临的问题是,相机的360度旋转在我对一个点的物理旋转之前就已经结束了。我试着通过下面的图片来描述它。

我怀疑四元数->欧拉角度转换导致了这个错误,而且四元数数学到目前为止还远远超出了我的理解。

我希望我已经提供了所有的信息,如果还需要更多的信息,我很高兴补充。

EN

回答 2

Stack Overflow用户

发布于 2015-01-23 16:58:49

感谢您对您的问题的描述和解决方案的开始!我非常确定,由于gimbal lock (松散的1个自由度),欧拉角不能做到这一点。

有效的解决方案是获得姿态并使用四元数来设置摄影机的方向:

代码语言:javascript
复制
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:myQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
    CMAttitude *currentAttitude = motion.attitude;
    [SCNTransaction begin];
    [SCNTransaction setDisableActions:YES];

    SCNQuaternion quaternion = [self orientationFromCMQuaternion:currentAttitude.quaternion];
    _cameraNode.orientation = quaternion;

    [SCNTransaction commit];
}];
- (SCNQuaternion)orientationFromCMQuaternion:(CMQuaternion)q
{
    GLKQuaternion gq1 =  GLKQuaternionMakeWithAngleAndAxis(GLKMathDegreesToRadians(-90), 1, 0, 0); // add a rotation of the pitch 90 degrees
    GLKQuaternion gq2 =  GLKQuaternionMake(q.x, q.y, q.z, q.w); // the current orientation
    GLKQuaternion qp  =  GLKQuaternionMultiply(gq1, gq2); // get the "new" orientation
    CMQuaternion rq =   {.x = qp.x, .y = qp.y, .z = qp.z, .w = qp.w}; 

    return SCNVector4Make(rq.x, rq.y, rq.z, rq.w);
}
票数 11
EN

Stack Overflow用户

发布于 2014-08-13 20:53:17

如果CoreMotion和SceneKit对欧拉角不使用相同的约定,那么您可能可以使用中间节点来简化转换。例如,通过如下的层次结构:

代码语言:javascript
复制
YawNode
  |
  |___PitchNode
      |
      |___RollNode
          |
          |___CameraNode

YawNode、PitchNode、RollNode将仅围绕一个轴旋转。然后,通过按层次顺序播放,您可以重现CoreMotion的约定。

话虽如此,我不确定转换四元数->欧拉是一个好主意。我怀疑在像0/ PI /2pi这样的角度下,你不会得到连续/平滑的过渡。我会尝试四元数->matrix4,并更新你的节点的“转换”。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25282543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档