ARCore 坐标系相关梳理
ARCore 坐标系指的是ARCore根据图像帧返回的位姿的定义系统,根据[官网](Pose | ARCore | Google Developers)介绍:随着 ARCore 对环境的理解发生变化,它会调整其世界模型以保持事物的一致性。发生这种情况时,摄像机和锚点的数字位置(坐标)会发生显着变化,以保持它们所代表的物理位置的适当相对位置。这些变化意味着每一帧都应该被认为是在一个完全独特的世界坐标空间中。锚点和相机的数字坐标不应在检索它们的渲染帧之外使用。如果需要考虑超出单个渲染帧范围的位置,则应创建锚点或使用相对于附近现有锚点的位置。那ARCore的坐标系从何而来呢?如何保证其绝对性呢?
另外ARCore本身返回的位姿是基于其坐标系的,但是AR应用的往往有不同的渲染引擎,如OpenGL SE、Unity、WebGL等,不同的渲染引擎使用的坐标系不一定一样,如OpenGL SE是右手坐标系、Unity则是左手。AR应用的核心有两个点,一个是把相机的视频流以背景方式渲染3D场景中,一个是虚拟相机的位姿和ARCore返回的位姿绑定,也就是ARCore的位姿要转换到不同的渲染引擎坐标空间中,它们又是如何转换的呢?
绝对的地理坐标系
在一定范围下,只考虑物体的经度、纬度,我们可以把各个物体视为近似在同一水平面上。我们以当前手机的位置为原点O,过O作地球球面的切面M,过O做经过O点经线的切线O->N,这个切线指向正北方向,过O做切面M的垂线O->M,该垂线指向地心。
首先我们以O为坐标系的原点,定义右手坐标系。以O->N的相反方向N->O作为X轴,X轴指向南,以O->M的相反方向M->O作为Z轴,Z轴反向指向地心,那么根据右手坐标系的定义,Y轴就指向东。
这还不是我们最终的世界坐标系,因为我们的手机的方向并不总是指向正北也就是我们上面定义的坐标系中的O->N方向,他有自己的方位角Azimuth angle(可以通过传感器获取),即手机与正北方向的夹角。所以,我们需要对XY轴的方向根据手机的状态进行调整,根据方位角,以Z轴为旋转轴进行旋转,使得旋转后的X轴指向与手机朝向相反。
ARCore 世界坐标系
在地理坐标系下,可以根据地磁场构建一个相对于地球的
绝对
的坐标系统,因此对于ARCore来说以此为基础也可以构建自己的一个绝对
世界坐标系ARCore的世界坐标系是以Y轴做了重力对齐,相当于地理坐标系的Z轴,反方向永远垂直指向地心。X轴正方向由西指向东,Z轴正方向由北指向南。地理坐标和ARCore 世界坐标系的转换可以根据IMU传感器数据来转换,具体转换过程还未深入,有待研究。ARCore 产生的基础位姿应该是基于世界坐标系的,这里说基础位姿是因为ARCore 应用接口里类似getDisplayOrientedPose、getAndroidSensorPose
的方法获取摄像头空间坐标系的位姿和安卓传感器坐标系下的位姿,这些位姿应该是基于基础位姿转换而来的,下面有具体代码参考。
渲染引擎一般坐标系
ARCore本身不做渲染,所以需要借助渲染引擎渲染AR应用。我们的手机屏幕是二维的,但是我们展示的是三维AR世界,当我们在构建一个AR场景时,是以一个三维世界既是世界坐标来构建,而转化为屏幕坐标展示在我们眼前,则需要经历多道矩阵变化,这就是渲染引擎做的事情。
世界坐标系:
在渲染引擎中,世界坐标系是以屏幕中心为原点(0, 0, 0),且是始终不变的。长度单位这样来定:窗口范围按此单位恰好是(-1,-1,-1)到(1,1,1)。面对屏幕,右手坐标系,你的右边是X正轴,上面是Y正轴,屏幕指向你的为Z正轴,左手坐标系X轴相反。
屏幕坐标系:
渲染引擎的重要功能之一就是将三维的世界坐标经过变换、投影等计算,最终算出它在显示设备上对应的位置,这个位置就称为设备坐标。在屏幕、打印机等设备上的坐标是二维坐标。
视点坐标系:
是以视点(照相机)为原点,以视线的方向渲染引擎会将世界坐标先变换到视点坐标,然后进行裁剪,只有在视线范围(视见体)之内的场景才会进入下一阶段的计算。
OpenGL SE 坐标系
OpenGL SE坐标系是右手坐标系,根据上面的介绍可知,其坐标系原点在手机屏幕中间,当面对屏幕的时候,右手边为X正轴,指向自己方向为Z轴正轴,上面是Y轴正轴,其坐标系与安卓传感器坐标系重合。因此ARCore提供了获取安卓传感器坐标系下的位姿方法。
ARCore 提供了
getAndroidSensorPose
方法来获取传感器坐标系下的位姿,其参考代码如下:Frame frame = mArFragment.getArSceneView().getArFrame(); Camera camera = frame.getCamera(); Pose T_gc = frame.getAndroidSensorPose();
Unity 坐标系
Unity 是左手坐标系,ARCore位姿需要转换到Unity坐标系下。在Unity的ARCore SDK是通过
getDisplayOrientedPose
获得位姿,获得位姿后通过转换函数转换为Unity 位姿,示例代码如下:public static void ApiPoseToUnityPose(ApiPoseData apiPose, out Pose unityPose) { Matrix4x4 glWorld_T_glLocal = Matrix4x4.TRS( new Vector3(apiPose.X, apiPose.Y, apiPose.Z), new Quaternion(apiPose.Qx, apiPose.Qy, apiPose.Qz, apiPose.Qw), Vector3.one); Matrix4x4 unityWorld_T_unityLocal = _unityWorldToGLWorld * glWorld_T_glLocal * _unityWorldToGLWorldInverse; Vector3 position = unityWorld_T_unityLocal.GetColumn(3); Quaternion rotation = Quaternion.LookRotation(unityWorld_T_unityLocal.GetColumn(2), unityWorld_T_unityLocal.GetColumn(1)); unityPose = new Pose(position, rotation); }
查看ARCore文档可以发现,获取位姿还有一个方法
GetPose
,和getDisplayOrientedPose
的区别如下:
getPose
:Returns the pose of the physical camera in world space for the latest frame. This is an OpenGL camera pose with +X pointing right, +Y pointing right up, and -Z pointing in the direction the camera is looking, with "right" and "up" being relative to the image readout in the usual left-to-right, top-to-bottom order. Specifically, this is the camera pose at the center of exposure of the center row of the image.
返回世界空间中物理相机的姿势,以获得最新帧。这是一个 OpenGL 相机姿势,右指 X,+Y 指向右上,-Z 指向相机的方向,"右"和"向上"相对于通常从左到右、从上到下顺序的图像读出。具体来说,这是相机在图像中心行中央曝光的姿势。
getDisplayOrientedPose
:Returns the virtual camera pose in world space for rendering AR content onto the latest frame. This is an OpenGL camera pose with +X pointing right, +Y pointing up, and -Z pointing in the direction the camera is looking, with "right" and "up" being relative to current logical display orientation.
返回虚拟相机在世界空间中的姿势,将 AR 内容渲染到最新帧中。这是一个 OpenGL 相机姿势,右指 X,+Y 指向和 -Z 指向相机的方向,"右"和"向上"相对于当前的逻辑显示方向。
大概的意思是
getPose
是以物理相机为参考的,物理相机旋转后其坐标系会旋转,getDisplayOrientedPose
是根据渲染画面为参考的,设备旋转的时候如果解锁画面跟随旋转的话,渲染画面也会旋转,所以坐标系是不变的。大概示意图如下:
参考文档
ThreeJS中的点击与交互——Raycaster的用法 - SegmentFault 思否
fixing-device-coordinate-system-regardless-of-device-orientation