最近在学习OpenXR标准并研究在Android上实现,现在基本功能已经实现,简单总结下。
官方网址介绍的挺好的,就不赘余了。
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:
Loader, 由khronos官方提供,下载地址https://github.com/KhronosGroup/OpenXR-SDK.
CTS,OpenXR 一致性检测,由khronos官方提供,下载地址https://github.com/KhronosGroup/OpenXR-CTS.git
HelloXR,khronos官方提供的OpenXR Demo,下载地址https://github.com/KhronosGroup/OpenXR-SDK-Source
…
…
为方便理解,我们结合OpenXR官方示例DemoHelloXR进行讲解。
首先附上Helloxr的运行效果
HelloXR是基于NativeActivity框架实现的Android应用,关于NativeActivity的相关知识这里就不介绍了,HelloXR的时序图如下:
我们现在HelloXR中一些比较主要的方法及其实现方案进行简要介绍
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对象或属性。
OpenXR 坐标系定义三种坐标系:
XR_REFERENCE_SPACE_TYPE_VIEW:
view坐标系,坐标原点为HMD左眼或者有眼,z轴指与视线相反,y轴垂直向上,x水平向右。
XR_REFERENCE_SPACE_TYPE_LOCAL
世界坐标系
XR_REFERENCE_SPACE_TYPE_STAGE
暂时与XR_REFERENCE_SPACE_TYPE_LOCAL相同,可以认为就是XR_REFERENCE_SPACE_TYPE_LOCAL
//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;
计算公式为:
//i=0时,表示左眼
//i=1时,表示右眼
views[i].pose.orientation = head.orientaion*eye[i].orientation
views[i].pose.position = head.orientaion*eye[i].position+ head.position
views[i].pose.orientation = eye[i].orientation
views[i].pose.position = eye[i].position
//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;
计算公式为:
location.pose.orientation = head.orientaion*space.orientation
location.pose.position = head.orientation*space.postion+ head.position
location.pose.orientation = space.orientation
location.pose.position = space.position
ProjectionMatrix=mat4_cast(views[i].fov)
公式:
ViewMatrix = mat4_cast(invert(views[i].pose))
ModelMatrix= mat4_cast(location.pose)
取逆的原因可以参考下二维旋转公式 ↩︎