开发

VR 焦点管理

提交应用程序时需提供必要的应用程序和元数据,以确保其能够在 Rift Store 列出,并从 Oculus Home 启动。
当从 Oculus Home 启动后,您需要编写一个循环来轮询会话状态。ovr_GetSessionStatus 返回包含以下布尔值的结构:
  • ShouldQuit - 如果应用程序应该发起关闭,则为 True。
  • HmdPresent - 如果存在 HMD,则为 True。
  • DisplayLost - 如果 HMD 已拔出,或显示驱动程序被手动禁用或出现 TDR,则为 True。
  • HmdMounted - 如果 HMD 戴在用户头上,则为 True。
  • IsVisible - 如果游戏或体验有 VR 焦点并在 HMD 中可见,则为 True。
  • ShouldRecenter - 如果应用程序应调用 ovr_RecenterTrackingOrigin,则为 True。这在用户通过通用菜单发起重新居中时触发。
  • OverlayPresent - 如果存在系统叠加(例如面板),则为 True。
  • DepthRequested - 如果运行时要求应用程序提供带投影层的深度缓冲区,则为 True。

用户退出时如何管理

ShouldQuittrue 时,保存应用程序状态并关闭,或者不保存状态直接关闭。用户将自动返回 Oculus Home。
依据应用程序的类型,您可以在下次打开时提示用户从上次中断处继续(如多关卡游戏),或者从体验起始点重新开始(如被动视频)。如果是多人游戏,则您可能想要在本地退出游戏,而不结束游戏。

用户请求重新居中时如何管理

ShouldRecentertrue 时,应用程序应调用 ovr_RecenterTrackingOriginovr_SpecifyTrackingOrigin,为以后根据不同的原点进行位置追踪做好准备。
某些应用程序可能会选择忽略此请求,或通过 ovr_RecenterTrackingOrigin 以外的内部机制来实现该请求。在此情况下,应用程序可调用 ovr_ClearShouldRecenterFlag 清除重新居中请求。

管理已拔出的头戴设备

如果 DisplayLosttrue
  1. 暂停应用程序,包括音频。
  2. 在显示器上显示头戴设备已拔出的提示。
  3. 销毁所有 TextureSwapChains 或镜像纹理。
  4. 调用 ovrDestroy
  5. 轮询 ovrSessionStatus::HmdPresent,直到 true
  6. 调用 ovrCreate 重新创建会话。
  7. 重新创建任何 TextureSwapChain 或镜像纹理。
  8. 恢复应用程序。
如果 ovrDetect 在指定时间内未返回 true,则按 ShouldQuit 返回 true 处理。如果用户未在指定时间内执行任何操作,则选择默认操作(保存会话或不保存直接关闭)并关闭应用程序。
:在多人游戏中,您可能想按照相同的流程进行,而不是暂停游戏。

管理不可用的头戴设备

如果用户取下头戴设备,或应用程序失去 VR 焦点,则 HmdMountedIsVisible 返回 false。暂停应用程序,直到返回 true
如果您的应用程序失去 VR 焦点,则自动停止接收输入。如果您的应用程序不使用 Oculus 输入 API,则应忽略任何接收到的输入。
:在多人游戏中,您可能希望游戏在不暂停的情况下继续。

Windows 焦点丢失时如何管理

当您的应用程序失去 Windows 焦点时,Oculus Remote、Xbox 控制器和 Touch 控制器仍会继续正常运作。但是,应用程序将无法继续控制鼠标和键盘。
如果您的应用程序失去 Windows 焦点但保持 VR 焦点(IsVisible),应继续接收输入,确保应用程序正常运行。如果继续操作需要键盘或鼠标,提示用户取下头戴设备并通使用 Alt-Tab 恢复 Windows 焦点。

管理面板和应用程序焦点

随着面板的引入,现在您的应用程序应表明其是否准备好响应 ovrSessionStatus 焦点状态,包括 ovrSessionStatus::HasInputFocus。如需了解更多信息,请参阅 Oculus Dash

代码示例

bool shouldQuit = false;

void RunApplication()
{
  ovrInitParams initParams = { ovrInit_RequestVersion | ovrInit_FocusAware, OVR_MINOR_VERSION, NULL, 0, 0 };
  ovrResult result = ovr_Initialize(&initParams);
  VALIDATE(OVR_SUCCESS(result), "Failed to initialize libOVR.");

    if (OVR_SUCCESS(result))
    {
        ovrSession session;
        ovrGraphicsLuid luid;
        result = ovr_Create(&session, &luid);
        result = ovr_WaitToBeginFrame(session, 0);
        result = ovr_BeginFrame(session, 0);

        if (OVR_SUCCESS(result))
        {
            ovrSessionStatus ss;

            <create graphics device with luid>
            <create render target via ovr_CreateTextureSwapChain>

            while (!shouldQuit)
            {
                <get next frame pose, e.g. via ovr_GetEyePoses>
                <render frame>

                result = ovr_EndFrame(...);

                if (result == ovrSuccess_NotVisible)
                {
                    <turn off audio output>
                    do { // Wait until we regain visibility or should quit
                        <sleep>
                        result = ovr_GetSessionStatus(session, &ss);
                        if (ss.ShouldQuit)
                            shouldQuit = true;
                    } while (OVR_SUCCESS(result) && !ss.IsVisible && !shouldQuit);
                    <possibly re-enable audio>
                }
                else if (result == ovrError_DisplayLost)
                {
                    // We can either immediately quit or do the following:
                    <destroy render target and graphics device>
                    ovr_Destroy(session);

                    do { // Spin while trying to recreate session.
                        result = ovr_Create(&session, &luid);
                    } while (OVR_FAILURE(result) && !shouldQuit);

                    if (OVR_SUCCESS(result))
                    {
                        <recreate graphics device with luid>
                        <recreate render target via ovr_CreateTextureSwapChain>
                    }
                }
                else if (OVR_FAILURE(result))
                {
                    shouldQuit = true;
                }

                ovr_GetSessionStatus(session, &ss);
                if (ss.ShouldQuit)
                    shouldQuit = true;
                if (ss.ShouldRecenter)
                {
                    ovr_RecenterTrackingOrigin(session); // or ovr_ClearShouldRecenterFlag(session) to ignore the request.
                    <do anything else needed to handle this>
                }
            }

            <destroy render target via ovr_DestroyTextureSwapChain>
            <destroy graphics device>

            ovr_Destroy(session);
        }

        ovr_Shutdown();
    }
}

OpenXR 焦点管理

应用程序在 OpenXR 下的行为预期与在 LibOVR CAPI 下相同,唯一的区别是 OpenXR API 主要通过会话状态变更事件来呈现焦点状态的变化。OpenXR 的会话状态与 LibOVR CAPI ovrSessionStatus 类似,具体如下:
  • ShouldQuit - XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED / XR_SESSION_STATE_EXITING
  • HmdPresent - 没有 OpenXR 对应功能。OpenXR 应用程序可使用 ovr_Detect。
  • DisplayLost - XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED / XR_SESSION_STATE_LOSS_PENDING
  • HmdMounted - XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED / XR_SESSION_STATE_READY,XR_SESSION_STATE_STOPPING
  • IsVisible - XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED / XR_SESSION_STATE_VISIBLE,XR_SESSION_STATE_SYNCHRONIZED
  • ShouldRecenter - XR_TYPE_EVENT_DATA_RECENTER_REQUESTED_OCULUS,由 XR_OCULUS_recenter_event 扩展启用。
  • HasInputFocus - XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED / XR_SESSION_STATE_FOCUSED,XR_SESSION_STATE_VISIBLE
  • OverlayPresent - 不需要或没有 OpenXR 对应功能。
  • DepthRequested - 没有 OpenXR 对应功能。当 XR_KHR_composition_layer_depth 扩展启用时,应始终提供深度缓冲区。
详情请参阅 OpenXR 规范