CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

アプリ開発者目線で、OpenXR1.0のサンプルコードの概要と動作を確認しました

前回、Windows Mixed Reality OpenXR Runtimeについて書きました。  

www.crossroad-tech.com

このときはソースコードの話がなかったので、今回はOpenXRのサンプルコードを元に動かした話を書きました。

以下の環境で動作を確認しています。

  • Open XR API (version 1.0)

  • Windows 10 Pro (version 1903)

  • Visual Studio 2017 (version 15.9.9)

  • HoloLens2 Emulator(10.0.18362.1021)

  • Windows Mixed Reality Acer AH100

1. 2019/8/4時点では、サンプルコードを動かせるのはWindows Mixed Realityヘッドセット(またはエミュレータ)のみ

Relation between sdk, devices, and OpenXR

大雑把に言うと、OpenXRのAPIは2種類あります。

(1) アプリ開発するとき、デバイスの差異を気にしなくてよくなるためのOpenXR API(Application Interface)

(2) XRデバイス製造ベンダーが、開発環境の差異を気にしなくてよくなるためのOpenXR API (Device Plugin Interface)

XRデバイス:HMD、スマートフォン、タブレットです。

(2)を実現するために、開発環境のPCやXRデバイスには、個別のRuntimeインストールが必要です。2019/8/4時点では、Runtimeが提供されているのはWindows Mixed RealityとHoloLens2エミュレータのため、動作確認対象もその2つのみです。

2. OpenXRサンプルコードのビルドと動作確認(Windows Mixed Reality)

前回の記事で紹介したRuntimeに入っていたサンプルコードがあると思っていたのですが、そちらは見つけられませんでした。今のところ、見つけられたのは、Microsoftから提供されている下記のリポジトリのみです。

github.com

このリポジトリには、コントローラの入力を検出して、Cubeを増やすというサンプルが入っています。ビルドすると、Windows Mixed Realityヘッドセット(またはエミュレータ)で動作を確認できます。

まず、Githubからクローンし、OpenXR-SDK-VisualStudio-master/samples/BasicXrApp/BasicXrApp_uwp.vcxprojをVisual Studio 2017で開きます。

開くと、このような警告が出ることがあります。この場合、指示に沿ってC++ Universal Windows Platform toolsをインストールします。

Notification of missing component on Visual Studio 2017

警告が消えたら、CPUタイプをx86、デプロイ先をLocal Machineに変更し、Build/Deploy Solutionをクリックします。

ビルドが成功するとWindowx Mixed Reality のポータルが起動して、Windows Mixed Reality ヘッドセットにCubeが表示されます。

このサンプルでは、コントローラとCubeが連動して動きます。ボタンを押すたびに、もう1つのCubeが固定表示されます。次にボタンを押すと、前回のCubeは消えて新たなcubeが固定表示されます。

少し変更してみます。Visual Studio 2017で以下のc++ファイルを開きます。

OpenXR-SDK-VisualStudio-master/samples/BasicXrApp/CubeGraphics.cpp

       //L34付近  
        constexpr XrVector3f Red{1, 0, 0};
        constexpr XrVector3f DarkRed{0.25f, 0, 0};
        constexpr XrVector3f Green{0, 1, 0};
        constexpr XrVector3f DarkGreen{0, 0.25f, 0};
        constexpr XrVector3f Blue{0, 0, 1};
        constexpr XrVector3f DarkBlue{0, 0, 0.25f};
        constexpr XrVector3f White{1, 1, 1}; //追加
       //L.58付近
        constexpr Vertex c_cubeVertices[] = {
        //  CUBE_SIDE(LTB, LBF, LBB, LTB, LTF, LBF, DarkRed)   // -X  //コメントアウト
        //  CUBE_SIDE(RTB, RBB, RBF, RTB, RBF, RTF, Red)       // +X  //コメントアウト
            CUBE_SIDE(LTB, LBF, LBB, LTB, LTF, LBF, White)   // -X  :追加
            CUBE_SIDE(RTB, RBB, RBF, RTB, RBF, RTF, White)       // +X:追加
            CUBE_SIDE(LBB, LBF, RBF, LBB, RBF, RBB, DarkGreen) // -Y
            CUBE_SIDE(LTB, RTB, RTF, LTB, RTF, LTF, Green)     // +Y
            CUBE_SIDE(LBB, RBB, RTB, LBB, RTB, LTB, DarkBlue)  // -Z
            CUBE_SIDE(LBF, LTF, RTF, LBF, RTF, RBF, Blue)      // +Z
        };

これで表示されるCubeの6面のうち2つの面が白色になります。

ここではコントローラを2つ使ってみました。左右どちらかのコントローラでボタンを押すと、前回のCubeが消えて、新しいCubeが表示されます。

3. 参考:OpenXRサンプルコードのビルドと動作確認(HoloLens2 エミュレータ)

こちらは動作しませんでしたが、参考までに構築方法を記載します。

まず、エミュレータを使うにはWindows 10 Pro(Homeでは動かない)であること、仮想化機能に対応したCPUであることが必要です。

3年近く前の記事ですが、確認方法は下記も参考ください。今回、当時と同じ構成のPCでHoloLens2エミュレータを起動できました。

www.crossroad-tech.com

インストール方法、およびエミュレータ概要は、下記にわかりやすくまとまってますのでご参考ください。

akihiro-document.azurewebsites.net

エミュレータの準備ができたら、Windows Mixed Realityのときに使ったVisual Studioプロジェクトを再起動して開きます。CPUをx86にすると、デプロイ先にHoloLens2 Emulatorが表示されます。

HoloLens2 Emulatorを選択してデプロイを実行すると、ビルド、デプロイが終了し、HoloLens2 Emulatorが起動します。
(起動には1分半前後かかりました)

しかし、このような表示が出て実行できませんでした。

Unhandled exception at 0x76F033F2 in BasicXrApp_uwp.exe: Microsoft C++ exception: std::logic_error at memory location 0x006FF220.

Exception unhandled of OpenXR1.0 on HoloLens2 emulator

ちなみに、ダメ元でCPUをx64にしてから実行しましたが、メモリアドレスが変わっただけで表示内容は同じでした。  

Unhandled exception at 0x00007FFEE664A839 in BasicXrApp_uwp.exe: Microsoft C++ exception: std::logic_error at memory location 0x000000A92CF7EAA8.  

エミュレータからメニューを開くとアプリアイコンは表示されるので、デプロイまでは成功しています。もし原因がわかったら追記します。

4. サンプルコードの一部を紹介

CubeGraphics.cpp とOpenXrProgram.cppが主なファイルで、Windows Mixed RealityのHMDとコントローラに関する処理が書かれているのはOpenXrProgram.cppです。代表的な箇所を抜粋しました。

4.1 コントローラの宣言

OpenXR-SDK-VisualStudio-master/samples/BasicXrApp/OpenXrProgram.cpp

//L153付近
// Setup suggest bindings for simple controller.
{
    std::vector<XrActionSuggestedBinding> bindings;
    bindings.push_back({m_placeAction.Get(), GetXrPath("/user/hand/right/input/select/click")});
    bindings.push_back({m_placeAction.Get(), GetXrPath("/user/hand/left/input/select/click")});
    bindings.push_back({m_poseAction.Get(), GetXrPath("/user/hand/right/input/grip/pose")});
    bindings.push_back({m_poseAction.Get(), GetXrPath("/user/hand/left/input/grip/pose")});
    bindings.push_back({m_vibrateAction.Get(), GetXrPath("/user/hand/right/output/haptic")});
    bindings.push_back({m_vibrateAction.Get(), GetXrPath("/user/hand/left/output/haptic")});

    XrInteractionProfileSuggestedBinding suggestedBindings{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
    suggestedBindings.interactionProfile = GetXrPath("/interaction_profiles/khr/simple_controller");
    suggestedBindings.suggestedBindings = bindings.data();
    suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
    CHECK_XRCMD(xrSuggestInteractionProfileBindings(m_instance.Get(), &suggestedBindings));
}

4.2 Cubeの宣言

OpenXR-SDK-VisualStudio-master/samples/BasicXrApp/OpenXrProgram.cpp

//L. 231付近
  void CreateSpaces() {
            CHECK(m_session.Get() != XR_NULL_HANDLE);

            // Create a space to place a cube in the world.
            {
//途中省略

//L252
// ここでcubeを宣言  
spaceCreateInfo.poseInReferenceSpace = Pose::Translation({0, 0, -1});
CHECK_XRCMD(xrCreateReferenceSpace(m_session.Get(), &spaceCreateInfo, m_placedCubeSpace.Put()));
m_placedCube.Space = m_placedCubeSpace.Get();
m_placedCube.Scale = {0.1f, 0.1f, 0.1f};
//途中省略  
//L. 622
 XrSpaceHandle m_placedCubeSpace;
Cube m_placedCube; // Placed in local or anchor space.

4.3 コントローラとcubeの対応づけ

OpenXR-SDK-VisualStudio-master/samples/BasicXrApp/OpenXrProgram.cpp

//L511付近
bool RenderLayer(XrTime predictedDisplayTime,
   XrCompositionLayerProjection& layer,
   std::vector<XrCompositionLayerProjectionView>& projectionLayerViews) {
//途中省略
//L534付近
// Update cubes location with latest space relation
for (auto cube : {m_placedCube, m_cubesInHand[LeftSide], m_cubesInHand[RightSide]}) {
    if (cube.Space != XR_NULL_HANDLE) {
        XrSpaceLocation spaceLocation{XR_TYPE_SPACE_LOCATION};
        CHECK_XRCMD(xrLocateSpace(cube.Space, m_sceneSpace.Get(), predictedDisplayTime, &spaceLocation));

        if (xr::math::Pose::IsPoseValid(spaceLocation)) {
            cube.Pose = spaceLocation.pose;
            visibleCubes.push_back(cube);
        }
    }
}

5. 参考:OpenXR APIリファレンスガイド

OpenXRのAPI(アプリ開発側)の名称や役割、関係性を知りたかったので調べたら下記を見つけました。

OpenXR API Overview

引用元:https://www.khronos.org/files/openxr-10-reference-guide.pdf

このpdfファイルには、HMDやコントローラの動作定義、イベント処理方法、グラフィック処理などのAPI名称、引数や簡単な使い方が書いてあります。後半にはアプリケーションのライフライクルも載っており、サンプルコードと合わせれば基本的な動きは理解できます。

たとえば、4.1-4.3で紹介したソースコードのXrActionSuggestedBinding、xrCreateReferenceSpace、XrSpaceLocationはpdfの中で解説されています。

6. おわりに

アプリ開発側のAPIの使い方がわかったことで、OpenXRの理解が少し進みました。
ざっと探しましたが、OpenXRには、Particleなどのエフェクトや、GUI開発環境への言及がありませんでした。そのため、OpenXRの普及が進んでも、アプリケーション開発はUnityやUnreal Engineなどが引き続き使われると思います。
そうすると、アプリ開発者としては、デバイスのRuntime対応、およびUnityやUnreal Engineなどの開発環境の対応が進んでから試すのが良さそうですね。