物理キーボードトラッキングの統合(廃止) 更新日時: 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モデルとしてレンダリングされます。手がキーボードの近くにある場合、レンダリングはパススルーモードを利用するように切り替わり、ユーザーには自分の実際の手が表示されます。
トラッキングキーボードでの操作を開始する前提として、以下が必要です。
V72以前のMeta Horizon OSが搭載されているMeta Quest 2、Quest Pro、またはQuest 3のヘッドセット。 Meta Questのトラッキングキーボード サポートページにリストされているキーボードのいずれか。ヘッドセットで[Settings (設定)] > [System (システム)] > [Software Update (ソフトウェアのアップデート)] に移動します。 バージョンを確認します。 バージョンが37以上ではない場合、ソフトウェアを入手可能な最新バージョンにアップデートしてください。 物理トラッキングキーボードのレンダリングの主要な機能を利用可能にするには、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から継承することをおすすめします。そうすれば、多くの便利なメソッドやオブジェクトにアクセスできるので、初心者の入門編として最適です。利用可能なオブジェクトの例として、
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_NAMEXR_FB_PASSTHROUGH_EXTENSION_NAMEXR_FB_TRIANGLE_MESH_EXTENSION_NAME ハンド
XR_EXT_HAND_TRACKING_EXTENSION_NAMEXR_FB_HAND_TRACKING_MESH_EXTENSION_NAMEXR_FB_HAND_TRACKING_AIM_EXTENSION_NAME コントローラーレイヤーブレンド
XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME これらの拡張機能は、XrAppのGetExtensionsメソッドをオーバーライドして結果を返し、すべての主要な拡張機能が必要なときに確実に返されるようにすることで公開できます。
// 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_)));
/// 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;
}
キーボードレンダリングモデルを読み込むには、まず利用可能なすべてのレンダリングモデルパスを列挙する必要があります。その際には、何度かの呼び出しによって、パスとそのプロパティを取得する必要があります。以下はその例です。
/// 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回目の呼び出しでバッファを取得します。
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);
前述のメソッドに示された引数programsとmaterialsは、利用目的に応じて異なる場合があります。以下に、使用可能な値の例をいくつか示します。
/// 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_);
}
または、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テクスチャーをサポートするには、
Khronos KTXライブラリ をプロジェクトに含め、
SUPPORTS_KTX2設定パラメーターを定義してKTX2の利用を明示的に宣言する必要があります。そうすることで、KTX2テクスチャーを解析して利用できるようになります。これを行わないと、KTX2テクスチャーは無視されます。