Android OpenXR 标准开发实现总结

最近在学习OpenXR标准并研究在Android上实现,现在基本功能已经实现,简单总结下。

1. OpenXR是什么:

官方网址介绍的挺好的,就不赘余了。
https://www.khronos.org/OpenXR
官方spec地址如下:
https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html

OpenXR主要作用就是统一不同厂商XR SDK接口,将不同厂商XR SDK API 统一到OpenXR API上

官方示意图如下,图片来自于https://www.khronos.org/OpenXR:
Android OpenXR 标准开发实现总结_第1张图片

1.0 OpenXR 组成

  1. Loader, 由khronos官方提供,下载地址https://github.com/KhronosGroup/OpenXR-SDK.

  2. Runtime,由不同厂商根据OpenXR 标准实现,
    如 Monado,Microsoft,Oculus Android OpenXR 标准开发实现总结_第2张图片

  3. CTS,OpenXR 一致性检测,由khronos官方提供,下载地址https://github.com/KhronosGroup/OpenXR-CTS.git

  4. HelloXR,khronos官方提供的OpenXR Demo,下载地址https://github.com/KhronosGroup/OpenXR-SDK-Source

1.1 OpenXR Loader介绍及NDK编译

1.2 OpenXR Runtime设计与实现

2. OpenXR接口介绍

为方便理解,我们结合OpenXR官方示例DemoHelloXR进行讲解。

2.1 HelloXR 运行效果图

首先附上Helloxr的运行效果

2.1 HelloXR 运行时序

HelloXR是基于NativeActivity框架实现的Android应用,关于NativeActivity的相关知识这里就不介绍了,HelloXR的时序图如下:
Android OpenXR 标准开发实现总结_第3张图片

2.2 OpenXR接口及实现

我们现在HelloXR中一些比较主要的方法及其实现方案进行简要介绍

  1. xrCreateInstance定义
XRAPI_ATTR XrResult XRAPI_CALL xrCreateInstance(
    const XrInstanceCreateInfo*                 createInfo,
    XrInstance*                                 instance);

在Android 平台调用xrCreateInstance来创建XrInstance时,需要开启XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME扩展,并将一个XrInstanceCreateInfoAndroidKHR对象赋值给createInfo.next,代码属下

    XrInstanceCreateInfoAndroidKHR instanceCreateInfoAndroid;
    instanceCreateInfoAndroid = {XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
    //将JavaVm及activity赋值给instanceCreateInfoAndroid
    //以传递给Runtime,Runtime会通过该JavaVM及activity获取一些必需的Java对象
    //如surface和Choreographer
    instanceCreateInfoAndroid.applicationVM = data->applicationVM;
    instanceCreateInfoAndroid.applicationActivity = data->applicationActivity;
    
    XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO};
    createInfo.next = &instanceCreateInfoAndroid;
    //extensions必须包含XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME
    createInfo.enabledExtensionCount = (uint32_t)extensions.size();
    createInfo.enabledExtensionNames = extensions.data();
    xrCreateInstance(&createInfo, &m_instance);

Android平台,OpenXR在xrCreateInstance创建XrInstance时,会将JavaVM和activity传递给Runtime,Runtime接着会通过该JavaVM和activity获取显示控件surface及Vsync信号接受对象Choreographer及其他一些必需的java对象或属性。

x.x OpenXR 坐标系定义

OpenXR 坐标系定义三种坐标系:

  1. XR_REFERENCE_SPACE_TYPE_VIEW:
    view坐标系,坐标原点为HMD左眼或者有眼,z轴指与视线相反,y轴垂直向上,x水平向右。

  2. XR_REFERENCE_SPACE_TYPE_LOCAL
    世界坐标系

  3. XR_REFERENCE_SPACE_TYPE_STAGE
    暂时与XR_REFERENCE_SPACE_TYPE_LOCAL相同,可以认为就是XR_REFERENCE_SPACE_TYPE_LOCAL

x.x OpenXR xrLocateViews定义及转换公式

  1. xrLocateViews定义
//session,应用创建的XrSession
//viewLocateInfo,主要包含预测时间及应用的基础坐标系
//viewState,表示预测状态标志位, position,orientation是否有效
//viewCapacityInput:view 数量,一般为2,表示左右眼
//views,获取的左右眼位置和姿态信息
XRAPI_ATTR XrResult XRAPI_CALL xrLocateViews(
    XrSession                                   session,
    const XrViewLocateInfo*                     viewLocateInfo,
    XrViewState*                                viewState,
    uint32_t                                    viewCapacityInput,
    uint32_t*                                   viewCountOutput,
    XrView*                                     views);

首先,我们介绍下XrViewLocateInfo结构体:

//displayTime:预测时间
//space:应用所在的基础坐标系。
typedef struct XrViewLocateInfo {
    XrStructureType             type;
    const void* XR_MAY_ALIAS    next;
    XrViewConfigurationType     viewConfigurationType;
    XrTime                      displayTime;
    XrSpace                     space;
} XrViewLocateInfo;

在调用xrLocateViews之前,首先需要创建一个应用的基础坐标系,并设置其初始位置和姿态。
Helloxr应用的基础坐标系m_appSpace:

//m_appSpace的初始位置
referenceSpaceType=XR_REFERENCE_SPACE_TYPE_LOCAL
pose.orientaion = {0,0,0,1}
pose.position= {0,0,0}

为了计算方便,建议m_appSpace就取上边的值,不然计算公式还需要考虑basepace的影响,比较麻烦!!在以后的计算中,我们全部假设基础坐标就为m_appSpace
xrLocateViews返回的是XrView对象views,其定义为

//pose:获取的view(表示左眼或者右眼)的位置姿态
//fov:获取的view(表示左眼或者右眼)的fov
typedef struct XrView {
    XrStructureType       type;
    void* XR_MAY_ALIAS    next;
    XrPosef               pose;
    XrFovf                fov;
} XrView;

计算公式为:

  1. xrLocateViews返回的是XrViewLocateInfo.space 到XR_REFERENCE_SPACE_TYPE_VIEW坐标系的转换关系,由于基础坐标系XrViewLocateInfo.space 为m_appSpace,类型为XR_REFERENCE_SPACE_TYPE_LOCAL ,xrLocateViews返回的就是XR_REFERENCE_SPACE_TYPE_LOCAL 坐标系到XR_REFERENCE_SPACE_TYPE_VIEW的转换关系(或者说,XR_REFERENCE_SPACE_TYPE_LOCAL 坐标系旋转多少角度和平移多少距离后与XR_REFERENCE_SPACE_TYPE_VIEW坐标系重合)
    //i=0时,表示左眼
    //i=1时,表示右眼
    views[i].pose.orientation     = head.orientaion*eye[i].orientation
    views[i].pose.position        = head.orientaion*eye[i].position+ head.position
  1. 如果基础坐标系XrViewLocateInfo.space 类型为XR_REFERENCE_SPACE_TYPE_VIEW,
    返回的是eye的位置和姿态,由于假定了XrViewLocateInfo.space为m_appSpace,该公式不会走到
    views[i].pose.orientation     = eye[i].orientation
    views[i].pose.position        = eye[i].position

x.x OpenXR xrLocateSpace定义及转换公式

  1. xrLocateSpace定义
//space:物体所在空间的初始位置姿态
//baseSpace:基础坐标空间,helloxr是m_appSpace
//time:预测时间
//location:获取的结果
XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpace(
    XrSpace                                     space,
    XrSpace                                     baseSpace,
    XrTime                                      time,
    XrSpaceLocation*                            location);

在调用xrLocateSpace,首先需要传入两个坐标空间,基础坐标空间baseSpace和物体所在的空间space。
helloxr 的基础坐标空间baseSpace为m_appSpace={XR_REFERENCE_SPACE_TYPE_LOCAL,{0,0,0,},{0,0,0,1}}。
xrLocateSpace返回的是XrSpaceLocation 对象location,其定义

//locationFlags 为获取的pose的质量
//pose         为获取的pose
typedef struct XrSpaceLocation {
    XrStructureType         type;
    void* XR_MAY_ALIAS      next;
    XrSpaceLocationFlags    locationFlags;
    XrPosef                 pose;
} XrSpaceLocation;

计算公式为:

  1. 如果space为XR_REFERENCE_SPACE_TYPE_VIEW坐标空间
    location.pose.orientation     = head.orientaion*space.orientation
    location.pose.position        = head.orientation*space.postion+ head.position
  1. 如果space为XR_REFERENCE_SPACE_TYPE_LOCAL坐标空间
    location.pose.orientation     = space.orientation
    location.pose.position        = space.position

x.x OpenXR MVP 矩阵计算

  1. ProjectionMatrix矩阵计算:直接通过views[i].fov
    公式:
ProjectionMatrix=mat4_cast(views[i].fov)
  1. ViewMatrix矩阵计算:
    在计算ViewMatrix矩阵时,不能直接使用views[i].pose,需要取下逆1

公式:

ViewMatrix = mat4_cast(invert(views[i].pose))
  1. ModelMatrix矩阵计算:
    公式:
ModelMatrix= mat4_cast(location.pose)

  1. 取逆的原因可以参考下二维旋转公式 ↩︎

你可能感兴趣的:(Android OpenXR 标准开发实现总结)