获得ARKit的Y旋转pointOfView

时间:2018-04-16 18:50:35

标签: rotation arkit euler-angles pointofview

我试图在ARKit场景(0 - 360度)中获得视角的真实生活角度。我使用了来自pointOfView的SCNNode的euler角度。

print("\(pointOfView.eulerAngles.y.radiansToDegrees)")

问题是,当向北看时,我得到的结果为0,当向南看时,我也得到0.当看NE时,我得到-45度,当看到SE时,我也得到-45度。似乎SCNNode无法确定南北之间,只能介于西方和东方之间。有什么建议吗?

我通常需要在我的ARKit现实世界场景中实现雷达视图。预期的行为是北:0,东:90,南:180,西:270。

提前致谢!

1 个答案:

答案 0 :(得分:2)

我一直在研究类似情况。我称之为“标题”之后的情况是什么,它不像您想的那样清晰定义。

快速背景:仅供参考,相对于现实空间有两种旋转方式,即“欧拉(Euler)”,但在“俯仰”极端时遭受了所谓的万向节锁定。然后存在ARCamera的transform属性中保持的相对于设备轴的旋转角度。

为了说明不同之处,euler.y始终表示设备所面对的方式(除非它是平的,否则万向架锁会将其弄乱,因此是我们的问题),而变换y始终表示通过电话绕垂直轴旋转(这只是使事情更加令人困惑,这是基于ARKit中保持 landscape 的设备)。

(注意:如果您习惯使用CoreMotion,您可能已经注意到,在ARKit中,将设备放平时会发生云台锁定,而在CM中则是直立的)。

那么,无论设备是扁平的还是直立的,我们如何获得可以使用的“标题”?下面的解决方案(很抱歉,它是Objective-c!)执行以下操作:

  1. 取两个法线向量,一个沿电话的Z轴(从屏幕直接伸出),另一个沿电话的底部伸出,我称它为-Y轴(尽管实际上是+ X轴)保持景观时。)

  2. 通过设备的变换(不是Eulers)旋转矢量,投影到XZ平面上,并获得投影矢量与Z轴的夹角。

  3. 当手机直立放置时,Z Normal将是理想的方向,但是当手机呈扁平形时,将使用Y Normal。在这两者之间,我们将基于手机的“倾斜”(即euler.x)进行“淡入淡出”。

  4. 一个小问题是,当用户将手机稍微向下放平,Z垂直翻转给出的方向时。我们并不是真的想要这样(从用户体验的角度出发,而不是从数学的角度出发),所以让我们检测这种“向下倾斜”,并在发生zHeading时翻转180˚。

无论设备朝向如何,最终结果都是一致且流畅的heading。甚至当设备在纵向和横向之间移动时,它也能正常工作...湿气!

// Create a Quaternion representing the devices curent rotation (NOT the same as the euler angles!)
GLKMatrix3 deviceRotM = GLKMatrix4GetMatrix3(SCNMatrix4ToGLKMatrix4(SCNMatrix4FromMat4(camera.transform)));
GLKQuaternion Q = GLKQuaternionMakeWithMatrix3(deviceRotM);

// We want to use the phone's Z normal (in the phone's reference frame) projected onto XZ to get the angle when the phone is upright BUT the Y normal when it's horizontal. We'll crossfade between the two based on the phone tilt (euler x)...
GLKVector3 phoneZNormal = GLKQuaternionRotateVector3(Q, GLKVector3Make(0, 0, 1));
GLKVector3 phoneYNormal = GLKQuaternionRotateVector3(Q, GLKVector3Make(1, 0, 0)); // why 1,0,0? Rotation=(0,0,0) is when the phone is landscape and upright. We want the vector that will point to +Z when the phone is portrait and flat

float zHeading = atan2f(phoneZNormal.x, phoneZNormal.z);
float yHeading = atan2f(phoneYNormal.x, phoneYNormal.z);

// Flip the zHeading if phone is tilting down, ie. the normal pointing down the device suddenly has a +y component
BOOL isDownTilt = phoneYNormal.y > 0;
if (isDownTilt) {
    zHeading = zHeading + M_PI;
    if (zHeading > M_PI) {
        zHeading -= 2 * M_PI;
    }
}

float a = fabs(camera.eulerAngles.x / M_PI_2);
float heading = a * yHeading + (1 - a) * zHeading;

NSLog(@"euler: %3.1f˚   %3.1f˚   %3.1f˚    zHeading=%3.1f˚    yHeading=%3.1f˚    heading=%3.1f˚    a=%.2f    status:%li:%li  zNorm=(%3.2f, %3.2f, %3.2f)    yNorm=(%3.2f, %3.2f, %3.2f)", GLKMathRadiansToDegrees(camera.eulerAngles.x), GLKMathRadiansToDegrees(camera.eulerAngles.y), GLKMathRadiansToDegrees(camera.eulerAngles.z), GLKMathRadiansToDegrees(zHeading), GLKMathRadiansToDegrees(yHeading), GLKMathRadiansToDegrees(heading), a, camera.trackingState, camera.trackingStateReason, phoneZNormal.x, phoneZNormal.y, phoneZNormal.z, phoneYNormal.x, phoneYNormal.y, phoneYNormal.z);