LoginSignup
4
2

More than 1 year has passed since last update.

OpenXR Unity編

Last updated at Posted at 2022-11-17

実装例

OpenXR 拡張を薄くラップする例。
詳しくはソース参照のこと。

openxr 拡張 実装されているデバイス 備考
XR_EXT_hand_tracking quest, quest2, quest pro, hololens2, HTC Vive Cosmos, HTC Vive Focus 3, Varjo など quest link でも動きます。leapmotion でも動いた
XR_FB_body_tracking quest, quest2, quest pro quest link でも動きます。Version 47 が必要な様子(Android側とPC側の両方)
XR_FB_eye_tracking_social quest pro quest link でも動きます。
XR_FB_face_tracking quest pro quest link でも動きます。

Unity の OpenXR

Unity で OpenXR API に直接アクセスするには公式の API からできます。

C の openxr/openxr.h の情報から Feature を自作して OpenXR Extension にアクセスする方法について。

下記のリポジトリで
XR_EXT_hand_trackingXR_FB_hand_tracking_mesh を実装を発見しました。
quest, quest proapkoculus link 双方で動作します。

本記事は、この実装を読み解いた解説的なものです。

OpenXRFeature

継承と属性で Feature を定義する

#if UNITY_EDITOR
    [UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Hand tracking Extension",
        BuildTargetGroups = new[] {
            UnityEditor.BuildTargetGroup.Standalone, UnityEditor.BuildTargetGroup.WSA, UnityEditor.BuildTargetGroup.Android },
        Company = "Joe M",
        Desc = "Enable hand tracking in unity",
        DocumentationLink = "https://docs.unity3d.com/Packages/com.unity.xr.openxr@0.1/manual/index.html",
        OpenxrExtensionStrings = xr_extension,
        Version = "0.0.1",
        FeatureId = featureId)]
#endif
    public class HandTrackingFeature : OpenXRFeature
    {
        public const string featureId = "com.joemarshall.handtracking"; // Unique な ID
        public const string xr_extension = "XR_EXT_hand_tracking"; // OPENXR の拡張名。拡張を使わないときは無くて良い。
    }

OpenXRFeature を有効化する

edit - ProjectSettings - XR Plug-in Management - OpenXR

features.jpg

OpenXRFeature にアクセスする

// MonoBehaviour.Start で取得するとよい
var feature = OpenXRSettings.Instance.GetFeature<HandTrackingFeature>();

feature はシングルトン的にひとつ存在する。

のだけど、手違いで複数のローダーが Asset に存在したりすると多重に生成されて変な動きをするかもしれない(した。)

Project 設定が Assest として作成される

openxr_loader.jpg

OpenXRFeature のライフサイクル

OnInstanceCreate

拡張が有効か問い合わせて無かったら false を返すことで、Feature を無効にできる。

        override protected bool OnInstanceCreate(ulong xrInstance)
        {
            instance_ = xrInstance;
            if (!OpenXRRuntime.IsExtensionEnabled(xr_extension))
            {
                Debug.LogWarning($"{xr_extension} is not enabled.");
                // Return false here to indicate the system should disable your feature for this execution.  
                // Note that if a feature is marked required, returning false will cause the OpenXRLoader to abort and try another loader.
                return false;
            }

            return true;
        }

機種判定じゃなくて、拡張判定にした方が対応する機種を増やせそう

https://github.khronos.org/OpenXR-Inventory/extension_support.html#matrix

拡張が無効な場合。

  • プラットフォームあるいはデバイスに拡張が無い
  • 拡張がアプリに許可されていない(quest pro の eye tracking など)

同じデバイスでも Runtime によって拡張が変わる

  • Oculus 経由と Steam 経由で拡張の内容がわりと違う

https://github.com/maluoi/openxr-explorer

hand tracking (quest1,2,pro), eye tracking(quest pro), face tracking(quest pro) などは oculus link 経由でも有効化できる。

OnSessionBegin

関数ポインタを取得

OpenXR の C の API そのまま。xrGetInstanceProcAddr 関数で関数ポインタを得る

            var getInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<PFN_xrGetInstanceProcAddr>(xrGetInstanceProcAddr);
            Func<string, IntPtr> getAddr = (string name) =>
            {
                IntPtr ptr;
                getInstanceProcAddr(instance_, name, out ptr);
                return ptr;
            };
            xrCreateHandTrackerEXT_ = Marshal.GetDelegateForFunctionPointer<Type_xrCreateHandTrackerEXT>(getAddr("xrCreateHandTrackerEXT"));
            xrDestroyHandTrackerEXT_ = Marshal.GetDelegateForFunctionPointer<Type_xrDestroyHandTrackerEXT>(getAddr("xrDestroyHandTrackerEXT"));
            xrLocateHandJointsEXT_ = Marshal.GetDelegateForFunctionPointer<Type_xrLocateHandJointsEXT>(getAddr("xrLocateHandJointsEXT"));

handle初期化など

XR_EXT_hand_tracking では右手と左手の handle ここで初期化する。

MainLoop

XR_EXT_hand_tracking では右手と左手の handle を使って joint の情報を得る。

SessionEnd

XR_EXT_hand_tracking では右手と左手の handle を開放する。

SessionBegin と SessionEnd は何度も起きる場合がある。

例えば HMD を外して、再び装着した場合などに再度始まる様子。

DestoryInstance

個別の技

xrWaitFrame の呼び出しに割り込んで FrameTime を得る

GetInstanceProcAddr の呼び出しに割り込むことができる。
xrWaitFrame が来たときに別の関数でラップして、その関数ポインターを返すことで XrFrameState を横取りできる。

protected override IntPtr HookGetInstanceProcAddr(IntPtr func)

long XrFrameState.predictedDisplayTime が HandTracking で joint を得るのに必要なのだが、得る方法が他に無い?

Delegate詳細

Marshal.GetDelegateForFunctionPointerIntPtr(Cの関数ポインター) から Delegate を作る。

xrCreateHandTrackerEXT_ = Marshal.GetDelegateForFunctionPointer<Type_xrCreateHandTrackerEXT>(getAddr("xrCreateHandTrackerEXT"));

C 関数の呼び出しの作法で定義を作る。

        /*typedef struct XrHandTrackerCreateInfoEXT {
            XrStructureType      type;
            const void*          next;
            XrHandEXT            hand;
            XrHandJointSetEXT    handJointSet;
        } XrHandTrackerCreateInfoEXT;*/
        [StructLayout(LayoutKind.Sequential)]
        internal struct XrHandTrackerCreateInfoEXT
        {
            public int stype;
            public IntPtr next;
            public int hand;
            public int handJointSet;
        }

        /*XrResult xrCreateHandTrackerEXT(
            XrSession                                   session,
            const XrHandTrackerCreateInfoEXT*           createInfo,
            XrHandTrackerEXT*                           handTracker);*/
        internal delegate int Type_xrCreateHandTrackerEXT(ulong session, in XrHandTrackerCreateInfoEXT createInfo, out ulong tracker);

下記の仕様もしくは、ヘッダを参照する。

まだ、Khronos の仕様に来ていない新しい拡張はベンダーの SDK のドキュメントやヘッダーに定義がある。

XR_FB_eye_tracking_social など。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2