開発

物理キーボードトラッキングの統合(廃止)

更新日時: 2025/01/23
Tracked Keyboard Deprecation
v72では、キーボードのトラッキング機能は廃止され、新しいGeneric Keyboard Tracker(Quest 3およびQuest 3Sで利用可能)が採用されました。
Unityとの統合には、MR Utility KitのTrackablesを使うことをおすすめします。
Nativeとの統合には、以下をご覧ください。
このドキュメントは今後更新されることはなく、削除される場合もあります。
OpenXRを使って、Meta Questアプリに物理キーボードトラッキングのネイティブAPIを統合する手順をガイドします。
ユーザーにトラッキングキーボードの豊かなエクスペリエンスを提供するには、設定する必要があるコンポーネントがいくつかあります。以下はその例です。
  • トラッキングキーボード
  • パススルー
トラッキングキーボードは、バーチャル空間内のキーボードそのものです。このコンポーネントは、物理空間内でキーボードを探し、ユーザーの選択したキーボードとのマッチングを試みます。物理キーボードをトラッキングしているコンポーネントは、バーチャル空間内でレンダリングされます。
ハンドコンポーネントは、バーチャル空間において、ユーザーの実際の手に対応するハンドモデルのトラッキングと表示をサポートします。
パススルー機能を利用すると、ユーザーの実際の手をパススルーカメラレイヤーを使ってバーチャル空間に表示することができます。手がキーボードの近くにない場合は、VRモデルとしてレンダリングされます。手がキーボードの近くにある場合、レンダリングはパススルーモードを利用するように切り替わり、ユーザーには自分の実際の手が表示されます。
OpenXR仕様の一部としての完全なAPIリファレンスについては、XR_FB_keyboard_trackingをご覧ください。

前提条件

トラッキングキーボードでの操作を開始する前提として、以下が必要です。
  • V72以前のMeta Horizon OSが搭載されているMeta Quest 2、Quest Pro、またはQuest 3のヘッドセット。
  • Meta Questのトラッキングキーボードサポートページにリストされているキーボードのいずれか。
必ず最新バージョンのMeta QuestオペレーティングシステムとOculus OpenXR Mobile SDKを使ってください。バージョンは次の方法で確認できます。
  1. ヘッドセットで[Settings (設定)] > [System (システム)] > [Software Update (ソフトウェアのアップデート)]に移動します。
  2. バージョンを確認します。
  3. バージョンが37以上ではない場合、ソフトウェアを入手可能な最新バージョンにアップデートしてください。

Androidの設定

物理トラッキングキーボードのレンダリングの主要な機能を利用可能にするには、AndroidManifest.xmlファイルに以下の機能とアクセス許可が必要です。
<!-- Tell the system this app can render passthrough --><uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true" /><!-- Tell the system this app uses render model extensions --><uses-feature android:name="com.oculus.feature.RENDER_MODEL" android:required="true" /><uses-permission android:name="com.oculus.permission.RENDER_MODEL" /><!-- Tell the system this app can handle tracked keyboards --><uses-feature android:name="oculus.software.trackedkeyboard" android:required="false" /><uses-permission android:name="com.oculus.permission.TRACKED_KEYBOARD" />
Androidプロジェクトファイルには、以下の例に示すように、OpenXRライブラリを手動で読み込むためのNativeActivityが指定されていなければなりません。
public class MainActivity extends android.app.NativeActivity {
 static {
   System.loadLibrary("openxr_loader");
 }
}

ネイティブ統合

XrAppの使用

アプリの主要クラスはXrAppから継承することをおすすめします。そうすれば、多くの便利なメソッドやオブジェクトにアクセスできるので、初心者の入門編として最適です。利用可能なオブジェクトの例として、XrInstanceオブジェクトが挙げられます。これは、OpenXR APIの多くの呼び出しで使うことができます。
class XrExampleApp : public OVRFW::XrApp
このクラスを実装すれば、GetInstance()を呼び出してXrInstanceを取得したり、GetSession()を呼び出してXrSessionを取得したりできます。
XrInstance* instance = GetInstance();
XrSession* session = GetSession();
詳しくは、インスタンスとセッションの作成をご覧ください。

拡張子

物理キーボードトラッキングの機能を利用するには、以下の拡張機能が必要です。
  • キーボードトラッキング
    • XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME
  • ダイナミックレンダリングモデル
    • XR_FB_RENDER_MODEL_EXTENSION_NAME
  • パススルーハンド
    • XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME
    • XR_FB_PASSTHROUGH_EXTENSION_NAME
    • XR_FB_TRIANGLE_MESH_EXTENSION_NAME
  • ハンド
    • XR_EXT_HAND_TRACKING_EXTENSION_NAME
    • XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME
    • XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME
  • コントローラーレイヤーブレンド
    • XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME
これらの拡張機能は、XrAppGetExtensionsメソッドをオーバーライドして結果を返し、すべての主要な拡張機能が必要なときに確実に返されるようにすることで公開できます。
// Returns a list of OpenXr extensions needed for this app
virtual std::vector<const char*> GetExtensions() override {
    std::vector<const char*> extensions = XrApp::GetExtensions();
    extensions.push_back(XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME);
    extensions.push_back(XR_FB_RENDER_MODEL_EXTENSION_NAME);
    extensions.push_back(XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME);
    extensions.push_back(XR_FB_PASSTHROUGH_EXTENSION_NAME);
    extensions.push_back(XR_FB_TRIANGLE_MESH_EXTENSION_NAME);
    extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME);
    extensions.push_back(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME);
    extensions.push_back(XR_FB_HAND_TRACKING_AIM_EXTENSION_ANEM);
    extensions.push_back(XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME);
    extensions.push_back(kbdExtension);
    return extensions;
}

初期化

OpenXRを呼び出して、XrInstanceを使って特定のコンポーネント拡張機能を初期化します。OpenXRでキーボードトラッキングを初期化するには、以下の拡張機能を呼び出す必要があります。
oxr(xrGetInstanceProcAddr(
    instance,
    "xrQuerySystemTrackedKeyboardFB",
    (PFN_xrVoidFunction*)(&xrQuerySystemTrackedKeyboardFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrCreateKeyboardSpaceFB",
    (PFN_xrVoidFunction*)(&xrCreateKeyboardSpaceFB_)));
同様に、トラッキングキーボードの使用時にユーザーがパススルーによって自分の手を表示できるようにするには、xrGetInstanceProcAddrを使ってパススルーを初期化する必要があります。以下はその例です。
/// passthrough
oxr(xrGetInstanceProcAddr(
    instance, "xrCreatePassthroughFB", (PFN_xrVoidFunction*)(&xrCreatePassthroughFB_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrDestroyPassthroughFB", (PFN_xrVoidFunction*)(&xrDestroyPassthroughFB_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrPassthroughStartFB", (PFN_xrVoidFunction*)(&xrPassthroughStartFB_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrPassthroughPauseFB", (PFN_xrVoidFunction*)(&xrPassthroughPauseFB_)));
/// layer
oxr(xrGetInstanceProcAddr(
    instance,
    "xrCreatePassthroughLayerFB",
    (PFN_xrVoidFunction*)(&xrCreatePassthroughLayerFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrDestroyPassthroughLayerFB",
    (PFN_xrVoidFunction*)(&xrDestroyPassthroughLayerFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrPassthroughLayerPauseFB",
    (PFN_xrVoidFunction*)(&xrPassthroughLayerPauseFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrPassthroughLayerResumeFB",
    (PFN_xrVoidFunction*)(&xrPassthroughLayerResumeFB_)));
/// style
oxr(xrGetInstanceProcAddr(
    instance,
    "xrPassthroughLayerSetStyleFB",
    (PFN_xrVoidFunction*)(&xrPassthroughLayerSetStyleFB_)));
/// geometry
oxr(xrGetInstanceProcAddr(
    instance,
    "xrCreateGeometryInstanceFB",
    (PFN_xrVoidFunction*)(&xrCreateGeometryInstanceFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrDestroyGeometryInstanceFB",
    (PFN_xrVoidFunction*)(&xrDestroyGeometryInstanceFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrGeometryInstanceSetTransformFB",
    (PFN_xrVoidFunction*)(&xrGeometryInstanceSetTransformFB_)));
/// Passthrough - keyboard hands function
oxr(xrGetInstanceProcAddr(
    instance, "xrPassthroughLayerSetKeyboardHandsIntensityFB", (PFN_xrVoidFunction*)(&xrPassthroughLayerSetKeyboardHandsIntensityFB_)));
/// Passthrough - mesh extension functions
/// mesh
oxr(xrGetInstanceProcAddr(
    instance, "xrCreateTriangleMeshFB", (PFN_xrVoidFunction*)(&xrCreateTriangleMeshFB_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrDestroyTriangleMeshFB", (PFN_xrVoidFunction*)(&xrDestroyTriangleMeshFB_)));
/// buffers
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshGetVertexBufferFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshGetVertexBufferFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshGetIndexBufferFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshGetIndexBufferFB_)));
/// update
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshBeginUpdateFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshBeginUpdateFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshEndUpdateFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshEndUpdateFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshBeginVertexBufferUpdateFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshBeginVertexBufferUpdateFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrTriangleMeshEndVertexBufferUpdateFB",
    (PFN_xrVoidFunction*)(&xrTriangleMeshEndVertexBufferUpdateFB_)));
同様に、ハンドについては次の方法に従います。
/// Hook up extensions for hand tracking
oxr(xrGetInstanceProcAddr(
    instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_)));
/// Hook up extensions for hand rendering
oxr(xrGetInstanceProcAddr(
    instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_)));
最後に、レンダリングモデルをサポートする拡張機能を追加します。
/// Hook up extensions for device settings
oxr(xrGetInstanceProcAddr(
    instance,
    "xrEnumerateRenderModelPathsFB",
    (PFN_xrVoidFunction*)(&xrEnumerateRenderModelPathsFB_)));
oxr(xrGetInstanceProcAddr(
    instance,
    "xrGetRenderModelPropertiesFB",
    (PFN_xrVoidFunction*)(&xrGetRenderModelPropertiesFB_)));
oxr(xrGetInstanceProcAddr(
    instance, "xrLoadRenderModelFB", (PFN_xrVoidFunction*)(&xrLoadRenderModelFB_)));
必要であれば、これらの拡張機能を初期化し、管理することができます。これらの機能は、それぞれ担当分野の異なるいくつかのヘルパークラスに分割することをおすすめします(必須ではありません)。

キーボードトラッキング情報のクエリシステム

トラッキングキーボード情報を受信してキーボードモデルをアップデートできるようにするには、システムをクエリする必要があります。そうすれば、キーボードが存在しているかどうか、存在しているのであればトラッキングできるかどうかを確認できます。さらに、後ほどキーボードのアップデート済みの状態値をクエリすることもできます。
if (xrQuerySystemTrackedKeyboardFB_) {
    // current query
    {
        XrKeyboardTrackingQueryFB queryInfo{XR_TYPE_KEYBOARD_TRACKING_QUERY_FB};
        queryInfo.flags = XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB;
        XrKeyboardTrackingDescriptionFB desc;
        if (oxr(xrQuerySystemTrackedKeyboardFB_(session_, &queryInfo, &desc))) {
            if ((desc.flags & XR_KEYBOARD_TRACKING_EXISTS_BIT_FB) != 0) {
                // found keyboard
                if (!systemKeyboardExists_ ||
                    systemKeyboardDesc_.trackedKeyboardId != desc.trackedKeyboardId ||
                    systemKeyboardDesc_.flags != desc.flags) {
                    ALOG(
                        "Found new system keyboard '%d' '%s'",
                        desc.trackedKeyboardId,
                        desc.name);
                    systemKeyboardExists_ = true;
                    systemKeyboardDesc_ = desc;
                    systemKeyboardConnected_ =
                        systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB;
                    if ((systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_LOCAL_BIT_FB)) {
                        trackingSystemKeyboard_ = false;
                        if (trackSystemKeyboard_) {
                            if (systemKeyboardConnected_ ||
                                !requireKeyboardConnectedToTrack_) {
                                if (StartTrackingSystemKeyboard()) {
                                    trackingSystemKeyboard_ = true;
                                }
                            }
                        }
                        if (!trackingSystemKeyboard_) {
                            StopTracking();
                        }
                    } else {
                        ALOG(
                            "Found new system keyboard '%d' '%s', but not tracking because it isn't local",
                            desc.trackedKeyboardId,
                            desc.name);
                    }

                    systemKeyboardStateChanged_ = true;
                }
            } else {
                // no keyboard
                if (systemKeyboardExists_) {
                    systemKeyboardExists_ = false;
                    if (trackSystemKeyboard_) {
                        StopTracking();
                        trackingSystemKeyboard_ = false;
                    }
                    systemKeyboardStateChanged_ = true;
                }
            }
        }
    }
}

if (keyboardSpace_ != XR_NULL_HANDLE) {
    location_.next = nullptr;
    return oxr(
        xrLocateSpace(keyboardSpace_, currentSpace, predictedDisplayTime, &location_));
}

トラッキングの開始と停止

以下のコードでは、システムに対してキーボードトラッキングの開始と停止を指示する方法の例を示しています。
bool StartTrackingSystemKeyboard() {
    /// delete old ...
    StopTracking();

    if (xrCreateKeyboardSpaceFB_ && systemKeyboardExists_) {
        XrKeyboardSpaceCreateInfoFB createInfo{XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB};
        createInfo.trackedKeyboardId = systemKeyboardDesc_.trackedKeyboardId;
        if (XR_SUCCEEDED(
                oxr(xrCreateKeyboardSpaceFB_(session_, &createInfo, &keyboardSpace_)))) {
            size_ = systemKeyboardDesc_.size;
            return true;
        }
    }

    return false;
}

bool StopTracking() {
    bool result = false;
    if (keyboardSpace_ != XR_NULL_HANDLE) {
        result = oxr(xrDestroySpace(keyboardSpace_));
        if (result) {
            keyboardSpace_ = XR_NULL_HANDLE;
        } else {
            ALOG("Failed to destroy keyboardSpace_ %p", keyboardSpace_);
        }
    }
    return result;
}

キーボードレンダリングモデルの読み込み

キーボードレンダリングモデルを読み込むには、まず利用可能なすべてのレンダリングモデルパスを列挙する必要があります。その際には、何度かの呼び出しによって、パスとそのプロパティを取得する必要があります。以下はその例です。
XR_TYPE_RENDER_MODEL_PROPERTIES_FBは、メソッドxrGetRenderModelPropertiesFB_によってデータが設定される構造体定義です。
/// Enumerate available models
XrInstance instance = GetInstance();
if (xrEnumerateRenderModelPathsFB_) {
    /// Query path count
    uint32_t pathCount = 0;
    oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, nullptr));
    if (pathCount > 0) {
        XRLOG("XrRenderModelHelper: found %u models ", pathCount);
        paths_.resize(pathCount);
        /// Fill in the path data
        oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, &paths_[0]));
        /// Get properties
        for (const auto& p : paths_) {
            XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB};
            XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop);
            if (result == XR_SUCCESS) {
                properties_.push_back(prop);
            }
        }
    }
}
パスが分かったら、キーボードレンダリングモデルをクエリすることができます。まず、2段階呼び出しパターンを実行し、レンダリングモデル用のバッファを取得します。最初の呼び出しでバッファのサイズを取得し、2回目の呼び出しでバッファを取得します。
パスを取得したら、modelKeyXrRenderModelLoadInfoFBオブジェクトに渡すことができます。このキーを使ってxrLoadRenderModelFB_を呼び出し、モデル自体を読み込むことができます。この使用例を以下に示します。
std::vector<uint8_t> buffer;
XrInstance instance = GetInstance();
for (const auto& p : paths_) {
    char buf[256];
    uint32_t bufCount = 0;
    // OpenXR two call pattern: first call gets buffer size, second call gets the buffer
    // data
    oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr));
    oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0]));
    std::string pathString = buf;
    if (pathString.rfind("/model_fb/keyboard", 0) == 0) {
        XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB};
        XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop);
        if (result == XR_SUCCESS) {
            if (prop.modelKey != XR_NULL_RENDER_MODEL_KEY_FB) {
                XrRenderModelLoadInfoFB loadInfo = {XR_TYPE_RENDER_MODEL_LOAD_INFO_FB};
                loadInfo.modelKey = prop.modelKey;

                XrRenderModelBufferFB rmb{XR_TYPE_RENDER_MODEL_BUFFER_FB};
                rmb.next = nullptr;
                rmb.bufferCapacityInput = 0;
                rmb.buffer = nullptr;
                if (oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) {
                    XRLOG(
                        "Loading modelKey %u size %u ",
                        prop.modelKey,
                        rmb.bufferCountOutput);
                    buffer.resize(rmb.bufferCountOutput);
                    rmb.buffer = (uint8_t*)buffer.data();
                    rmb.bufferCapacityInput = rmb.bufferCountOutput;
                    if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) {
                        XRLOG(
                            "FAILED to load modelKey %u on pass 2",
                            prop.modelKey);
                        buffer.resize(0);
                    }
                }
            }
        }
    }
}
上記のコードが正常に実行されると、ユーザーの選択したキーボードがサポート対象であれば、そのキーボード用の未加工レンダリングモデルデータを含んだデータバッファを取得します。この未加工データは、モデルファイルから取得します。このデータは、使用する前に以下のセクションで示すように解析する必要があります。

解析とレンダリング

キーボードモデルをレンダリングするには、まずデータを解析する必要があります。すべてのキーボードモデルのフォーマットは*.glbであるため、glbを解析するためのメソッドを直接呼び出すことができます。
KeyboardModel = LoadModelFile_glB(
    "keyboard", (const char*)buffer.data(), buffer.size(), programs, materials);
前述のメソッドに示された引数programsmaterialsは、利用目的に応じて異なる場合があります。以下に、使用可能な値の例をいくつか示します。
/// Shader
ovrProgramParm UniformParms[] = {
    {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED},
    {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3},
    {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3},
    {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3},
    {"Opacity", ovrProgramParmType::FLOAT},
    {"AlphaBlend", ovrProgramParmType::FLOAT},
};
ProgKeyboard = GlProgram::Build(
    "",
    Keyboard::VertexShaderSrc,
    "",
    Keyboard::FragmentShaderSrc,
    UniformParms,
    sizeof(UniformParms) / sizeof(ovrProgramParm));

MaterialParms materials = {};
ModelGlPrograms programs = {};
programs.ProgSingleTexture = &ProgKeyboard;
programs.ProgBaseColorPBR = &ProgKeyboard;
programs.ProgSkinnedBaseColorPBR = &ProgKeyboard;
programs.ProgLightMapped = &ProgKeyboard;
programs.ProgBaseColorEmissivePBR = &ProgKeyboard;
programs.ProgSkinnedBaseColorEmissivePBR = &ProgKeyboard;
programs.ProgSimplePBR = &ProgKeyboard;
programs.ProgSkinnedSimplePBR = &ProgKeyboard;
データを解析した結果、複数のモデルが見つかる場合があります。以下の例に示すように、追加の設定を加えることで、返されるすべてのモデルを設定できます。
for (auto& model : KeyboardModel->Models) {
    auto& gc = model.surfaces[0].surfaceDef.graphicsCommand;
    gc.UniformData[0].Data = &gc.Textures[0];
    gc.UniformData[1].Data = &SpecularLightDirection;
    gc.UniformData[2].Data = &SpecularLightColor;
    gc.UniformData[3].Data = &AmbientLightColor;
    gc.UniformData[4].Data = &Opacity;
    gc.UniformData[5].Data = &AlphaBlendFactor;
    gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true;
    gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE;
    gc.GpuState.blendMode = GL_FUNC_ADD;
    gc.GpuState.blendSrc = GL_ONE;
    gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA;
}

/// Set defaults
SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f);
SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f;
AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f;
最後に、以下のメソッドを使ってモデルをレンダリングすることができます。
virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override {
    if (ShowModel && KeyboardModel != nullptr) {
        for (auto& model : KeyboardModel->Models) {
            ovrDrawSurface controllerSurface;
            controllerSurface.surface = &(model.surfaces[0].surfaceDef);
            controllerSurface.modelMatrix = Transform;
            out.Surfaces.push_back(controllerSurface);
        }
    }
}

アップデート

キーボードのレンダリングに加え、キーボードの状態のアップデートをトラッキングする必要があります。アップデートの例として、有効性の検証や3D空間での配置などが挙げられます。キーボードをアップデートすることで、ユーザーにリアルタイムで常に正しい状態のキーボードを表示することができます。
以下の例では、特定のアップデートフレームの空間と時刻を取得します。次に、クエリ対象のキーボードの最新の空間と配置が有効であるかどうかをチェックします。有効であれば、キーボードのポーズを該当フレームについてシステムから返された値にアップデートします。
virtual void Update(const OVRFW::ovrApplFrameIn& in) override {
   XrSpace currentSpace = GetCurrentSpace();
   XrTime predictedDisplayTime = GetPredictedDisplayTime();
   const XrSpaceLocationFlags isTracked =
           XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
   const XrSpaceLocationFlags isValid =
           XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
   XrSpaceLocationFlags flags = isTracked | isValid;

   if ((keyboardSpace_ != XR_NULL_HANDLE) && (location_.locationFlags & flags)) {
           renderKeyboard_ = true;
           std::vector<OVR::Posef> keyboardPoses;
           // Tracked joints and computed joints can all be valid
           XrSpaceLocationFlags isValid =
               XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
           if ((location_
.locationFlags & isValid) != 0) {
               /// render a box
               pose_ = FromXrPosef(location_
.pose);
               dimensions_ = FromXrVector3f(keyboard_->Size());
               /// add center
               keyboardPoses.push_back(pose_);
               /// add corners
               OVR::Posef point;
               point = OVR::Posef::Identity();
               point.Translation.x += dimensions_.x / 2.0f;
               point.Translation.z += dimensions_.z / 2.0f;
               keyboardPoses.push_back(pose_ * point);
               point = OVR::Posef::Identity();
               point.Translation.x += dimensions_.x / 2.0f;
               point.Translation.z -= dimensions_.z / 2.0f;
               keyboardPoses.push_back(pose_ * point);
               point = OVR::Posef::Identity();
               point.Translation.x -= dimensions_.x / 2.0f;
               point.Translation.z += dimensions_.z / 2.0f;
               keyboardPoses.push_back(pose_ * point);
               point = OVR::Posef::Identity();
               point.Translation.x -= dimensions_.x / 2.0f;
               point.Translation.z -= dimensions_.z / 2.0f;
               keyboardPoses.push_back(pose_ * point);
           }
   }

   /// Compute transform for the root
   Transform = Matrix4f(pose_);
}

パススルーキーボードハンド

Meta Questシステムでは、XR_FB_PASSTHROUGH_KEYBOARD_HANDS拡張機能を使用することで、手のパススルービューをキーボードの上に重ねてレンダリングすることができます。そのためには、XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARDS_HANDS_FBタイプの新しいパススルーレイヤーを作成します。xrPassthroughLayerSetKeyboardHandsIntensityFB関数により、それぞれの手の明度を設定できます。

パススルーウィンドウ

または、GeometryRendererを使ってパススルーウィンドウ枠を作成することもできます。その上で、これらをレンダリングする場所となる平面を、アップデートメソッドで更新します。
初期化:
/// setup geometry renderer for passthrough cutout
OVRFW::GeometryRenderer gr_;
gr_.Init(OVRFW::BuildTesselatedQuadDescriptor(4, 4, false)); // quad in XY plane, facing +Z
gr_.DiffuseColor = {1.0f, 1.0f, 1.0f, 1.0f};
gr_.ChannelControl = {0, 1, 0, 1};
gr_.AmbientLightColor = {1, 1, 1};
gr_.BlendMode = GL_FUNC_REVERSE_SUBTRACT;
更新
/// update cut out plane pose
OVR::Posef planePose = pose_;
planePose.Translation.y -= 0.02f;
planePose.Rotation *= OVR::Quatf({1.0f, 0.0f, 0.0f}, OVR::DegreeToRad(-90.0f));
gr_.SetPose(planePose);
const float padding = 0.1f; // provide some padding
gr_.SetScale(
    {(dimensions_.x * 0.5f) + padding,
     (dimensions_.z * 0.5f) + padding,
     1.0f});
gr_.Update();
レンダリング
gr_.Render(out.Surfaces);

KTX2サポート

KTX2テクスチャーをサポートするには、Khronos KTXライブラリをプロジェクトに含め、SUPPORTS_KTX2設定パラメーターを定義してKTX2の利用を明示的に宣言する必要があります。そうすることで、KTX2テクスチャーを解析して利用できるようになります。これを行わないと、KTX2テクスチャーは無視されます。
ナビゲーションロゴ
日本語
© 2026 Meta