C++ で OpenXR アプリを作るメモ。
Quest
向けに GLES3
+ NDK
を主に。
OpenXRのパターン
Swapchain を作る
- entry_point(main, android_main)
- Graphics 初期化(OpenGL コンテキストや D3Dデバイス初期化)
- XrInstance: インスタンス作成
- XrSystemID 取得
- XrSession 作成(Graphicsに依存, XrInstanceに対象のGraphics拡張を入れる必要あり)
- XrSwapchain 初期化(Graphicsに依存, XrInstanceに対象のGraphics拡張を入れる必要あり)
- XrInstance: Graphics拡張を指定する
- XrSystemID: どんなGraphicsに対応しているかの情報を含む
- XrSession, XrSwapchain: Graphics に依存して生成する
OpenGL, OpenGLES, D3D11, D3D112, Vulkan 等のどれを使うかを最初に決定させるのがシンプル。
XrSystemID
の情報を見て複数の Graphics をスイッチすることもできるが自作ではそこまでしなくてよいだろう。
Mainloop
- Mainloop
- Platform の EventHandling(Android, glfw...etc)
- OpenXR の EventHandling(session の active を判定)
- if XrSession active
- xrWaitFrame
- xrBeginFrame
- EachView(left, right...)
- xrWaitSwapchainImage
- xrAcquireSwapchainImage
- render(OpenGLなど)
- xrReleaseSwapchainImage
- xrEndFrame
初期化
XrInstance m_instance;
XrSession m_session;
XrSystemId m_systemId;
XrResult
すべて?の OpenXR 関数は XrResult で成功したかどうか返す。
typedef enum XrResult {
XR_SUCCESS = 0,
// 省略
} XrResult;
// error 文字列
if (XR_FAILED(ret)) {
char errbuf[XR_MAX_RESULT_STRING_SIZE];
xrResultToString(m_instance, ret, errbuf);
// logger
}
xrInitializeLoaderKHR
// Initialize the loader for this platform
PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
if (XR_SUCCEEDED(
xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader)))) {
XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
loaderInitInfoAndroid.next = NULL;
loaderInitInfoAndroid.applicationVM = app->activity->vm;
loaderInitInfoAndroid.applicationContext = app->activity->clazz;
initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);
}
xrCreateInstance
struct android_app *app;
XrInstanceCreateInfoAndroidKHR ciAndroid = {
.type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
.applicationVM = app->activity->vm,
.applicationActivity = app->activity->clazz,
};
std::array<const char *, 2> extensions = {
"XR_KHR_android_create_instance",
"XR_KHR_opengl_es_enable",
};
XrInstanceCreateInfo ci = {
.type = XR_TYPE_INSTANCE_CREATE_INFO,
.next = &ciAndroid,
.applicationInfo =
{
.apiVersion = XR_CURRENT_API_VERSION,
},
.enabledExtensionCount = (uint32_t)extensions.size(),
.enabledExtensionNames = extensions.data(),
};
strncpy(ci.applicationInfo.applicationName, "OXR_GLES_APP",
XR_MAX_ENGINE_NAME_SIZE - 1);
auto xrResult = xrCreateInstance(&ci, &m_instance);
xrGetSystem
XrSystemGetInfo sysInfo = {
.type = XR_TYPE_SYSTEM_GET_INFO,
.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY,
};
auto xrResult = xrGetSystem(m_instance, &sysInfo, &m_systemId);
Graphics初期化
xrCreateSession
View
Swapchain
以下のような構成になっている。
leftView
swapchain
[swapchainImage, swapchainImage, ...]
rightView
swapchain
[swapchainImage, swapchainImage, ...]
xrWaitSwapchainImage
xrAcquireSwapchainImage
render
XrSwapchainImage に対して XrView で描画する。
XrView
projection matrix
typedef struct XrFovf {
float angleLeft; // 👈 負
float angleRight;
float angleUp;
float angleDown; // 👈 負
} XrFovf;
view matrix
xrReleaseSwapchainImage
Frame
Event
xrWaitFrame, xrBeginFrame
xrLocateViews, xrLocateSpace
xrEndFrame
Space
原点を定義する概念。
XR_REFERENCE_SPACE_TYPE_VIEW
HMD の View を原点とする。HUD など画面に貼る付ける要素に使う
XR_REFERENCE_SPACE_TYPE_LOCAL
seated-scale 向け。初期位置を原点とする。
recentering できる。XrEventDataReferenceSpaceChangePending
event
XR_REFERENCE_SPACE_TYPE_STAGE
standing-scale, room-scale 向け。
地面に四角形を配置し、その中心が原点。
ユーザーが事前に設定したもの。
設定が無いときは参照できない。
拡張
拡張の一覧や対応状況がまとめられている
XR_EXT_hand_tracking
参考
openxr を使ったアプリが整理されていて、大変参考になります。
マルチプラットフォームの OpenXR-SDK の hello_xr よりも quest 向けで読み易いのでおすすめ。
-
OpenXRMobileSDK-0.38
cmake
https://github.com/terryky/android_openxr_gles