获取 HolographicSpace

注意

本文与旧版 WinRT 原生 API 相关。 对于新的本机应用项目,建议使用 OpenXR API

HolographicSpace 类是进入全息世界的门户。 它控制沉浸式呈现,提供相机数据,并提供对空间推理 API 的访问。 你需要为 UWP 应用的 CoreWindow 或 Win32 应用的 HWND 创建一个 HolographicSpace 类。

设置全息空间

创建全息空间对象是创建 Windows Mixed Reality 应用的第一步。 传统 Windows 应用会呈现到为其应用程序视图的核心窗口创建的 Direct3D 交换链。 此交换链会显示到全息 UI 中的盖板。 若要使应用程序视图成为全息视图而不是 2D 盖板视图,请为其核心窗口创建全息空间而不是交换链。 显示此全息空间创建的全息帧会将应用置于全屏呈现模式。

对于从全息 DirectX 11 应用(通用 Windows)模板着手的 UWP 应用,请在 AppView.cpp 的 SetWindow 方法中查找以下代码

m_holographicSpace = HolographicSpace::CreateForCoreWindow(window);

若要从 BasicHologram Win32 示例着手生成 Win32 应用,请在 App::CreateWindowAndHolographicSpace 中查找 HWND 示例。 然后,可以通过创建关联的 HolographicSpace 将其转换为沉浸式 HWND:

void App::CreateWindowAndHolographicSpace(HINSTANCE hInstance, int nCmdShow)
{
    // Store the instance handle in our class variable.
    m_hInst = hInstance;

    // Create the window for the HolographicSpace.
    hWnd = CreateWindowW(
        m_szWindowClass, 
        m_szTitle,
        WS_VISIBLE,
        CW_USEDEFAULT, 
        0, 
        CW_USEDEFAULT, 
        0, 
        nullptr, 
        nullptr, 
        hInstance, 
        nullptr);

    if (!hWnd)
    {
        winrt::check_hresult(E_FAIL);
    }

    {
        // Use WinRT factory to create the holographic space.
        using namespace winrt::Windows::Graphics::Holographic;
        winrt::com_ptr<IHolographicSpaceInterop> holographicSpaceInterop =
            winrt::get_activation_factory<HolographicSpace, IHolographicSpaceInterop>();
        winrt::com_ptr<ABI::Windows::Graphics::Holographic::IHolographicSpace> spHolographicSpace;
        winrt::check_hresult(holographicSpaceInterop->CreateForWindow(
            hWnd, __uuidof(ABI::Windows::Graphics::Holographic::IHolographicSpace),
            winrt::put_abi(spHolographicSpace)));

        if (!spHolographicSpace)
        {
            winrt::check_hresult(E_FAIL);
        }

        // Store the holographic space.
        m_holographicSpace = spHolographicSpace.as<HolographicSpace>();
    }

    // The DeviceResources class uses the preferred DXGI adapter ID from the holographic
    // space (when available) to create a Direct3D device. The HolographicSpace
    // uses this ID3D11Device to create and manage device-based resources such as
    // swap chains.
    m_deviceResources->SetHolographicSpace(m_holographicSpace);

    // The main class uses the holographic space for updates and rendering.
    m_main->SetHolographicSpace(hWnd, m_holographicSpace);

    // Show the window. This will activate the holographic view and switch focus
    // to the app in Windows Mixed Reality.
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
}

获取 UWP CoreWindow 或 Win32 HWND 的 HolographicSpace 后,可以通过 HolographicSpace 来处理全息相机、创建坐标系以及执行全息呈现操作。 当前的全息空间在 DirectX 模板中的多个位置使用:

  • DeviceResources 类需要从 HolographicSpace 对象获取一些信息才能创建 Direct3D 设备。 这是与全息显示器关联的 DXGI 适配器 ID。 HolographicSpace 类使用应用的 Direct3D 11 设备创建和管理基于设备的资源,例如每个全息相机的后台缓冲区。 如果你有兴趣了解此函数在后台的作用,可以在 DeviceResources.cpp 中找到它。
  • 函数 DeviceResources::InitializeUsingHolographicSpace 演示如何通过查找 LUID 来获取适配器,以及如何在未指定首选适配器时选择默认适配器
  • 应用的 main 类使用 AppView::SetWindow 或 App::CreateWindowAndHolographicSpace 中的全息空间实现更新和呈现

注意

虽然以下部分提及的来自模板的函数名称(例如 AppView::SetWindow)假定你从全息 UWP 应用模板着手,但你看到的代码片段将同等地适用于 UWP 和 Win32 应用

接下来,我们将深入探讨 SetHolographicSpace 在 AppMain 类中负责的设置过程

订阅相机事件、创建相机资源和删除相机资源

应用的全息内容位于其全息空间中,可以通过一个或多个全息相机进行查看,这些相机代表对场景的不同透视。 现在,你已拥有全息空间,可以接收全息相机的数据了。

应用需要通过创建特定于该相机的任何资源来响应 CameraAdded 事件。 此类资源的一个示例是后台缓冲区呈现目标视图。 在应用创建任何全息帧之前,可以在通过 AppView::SetWindow 进行调用的 DeviceResources::SetHolographicSpace 函数中查看以下代码

m_cameraAddedToken = m_holographicSpace.CameraAdded(
    std::bind(&AppMain::OnCameraAdded, this, _1, _2));

应用还需要通过释放为该相机创建的资源来响应 CameraRemoved 事件

在 DeviceResources::SetHolographicSpace 中

m_cameraRemovedToken = m_holographicSpace.CameraRemoved(
    std::bind(&AppMain::OnCameraRemoved, this, _1, _2));

事件处理程序必须完成一些工作才能使全息呈现和应用呈现顺利进行。 请阅读代码和注释来了解详细信息:可以在 main 类中查找 OnCameraAdded 和 OnCameraRemoved,以了解 DeviceResources 如何处理 m_cameraResources 映射

现在,我们重点关注的是 AppMain 以及它为了让应用感知全息相机而进行的设置。 因此,必须注意以下两个要求:

  1. 对于 CameraAdded 事件处理程序,应用可以异步工作,以完成新全息相机的资源创建和资产加载操作。 需要多个帧才能完成此工作的应用应请求延迟,并应在进行异步加载后完成延迟的工作。 PPL 任务可用于完成异步工作。 应用必须确保已准备好在退出事件处理程序或完成延迟工作后立即呈现到该相机。 退出事件处理程序或完成延迟工作是在告知系统:应用现在已准备好接收包含该相机的全息帧。

  2. 应用收到 CameraRemoved 事件后,必须立即释放对后台缓冲区的所有引用并退出函数。 这包括呈现目标视图,以及可能包含对 IDXGIResource 的引用的任何其他资源。 应用还必须确保后台缓冲区未作为呈现目标附加,如 CameraResources::ReleaseResourcesForBackBuffer 中所示。 为了加快操作速度,应用可以释放后台缓冲区,然后启动任务,以便通过异步方式为相机完成任何其他的清理工作。 全息应用模板包含可用于此目的的 PPL 任务。

注意

若要确定何时在帧上显示添加的或删除的相机,请使用 HolographicFrame 的 AddedCamerasRemovedCameras 属性

为全息内容创建参照系

应用的内容必须置于空间坐标系中才能在 HolographicSpace 中呈现。 系统提供两个可用于为全息影像建立坐标系的主要参照系。

Windows Holographic 中有两种参照系:附加到设备的参照系,以及设备在用户的环境中移动时保持静止的参照系。 全息应用模板默认使用静止参照系,这是呈现世界锁定全息影像的最简单方法之一。

静止参照系旨在稳定设备当前位置附近的位置。 这意味着,当设备感知其周围的空间细节后,离设备较远的坐标可能存在轻微偏差(相对于用户的环境而言)。 有两种方法可以创建静止参照系:从空间场地获取坐标系,或使用默认的 SpatialLocator。 如果要为沉浸式头戴显示设备创建 Windows Mixed Reality 应用,建议从空间场地着手。 空间场地还提供玩家佩戴的沉浸式头戴显示设备的功能的相关信息。 在这里,我们介绍如何使用默认的 SpatialLocator

空间定位器代表 Windows Mixed Reality 设备,可跟踪设备的运动,并提供可以相对于设备位置进行理解的坐标系。

在 AppMain::OnHolographicDisplayIsAvailableChanged 中

spatialLocator = SpatialLocator::GetDefault();

在应用启动时创建一次静止参照系。 这类似于定义世界坐标系,在应用启动时将原点置于设备的位置。 此参照系不随设备一起移动。

在 AppMain::SetHolographicSpace 中

m_stationaryReferenceFrame =
    m_spatialLocator.CreateStationaryFrameOfReferenceAtCurrentLocation();

所有参照系都是重力对齐的,这意味着 y 轴指向“上”(相对于用户的环境而言)。 由于 Windows 使用“右手”坐标系,因此 z 轴的方向与创建参照系时设备面向的“前进”方向一致。

注意

当应用需要精确放置单个全息影像时,请使用 SpatialAnchor 将单个全息影像定位到现实世界中的位置。 例如,当用户指示某个点为特殊兴趣点时,请使用空间定位点。 定位点位置不会偏移,但可以调整。 默认情况下,在调整定位点时,该定位点会在更正操作发生后在后续的几个帧中缓缓移到相应的位置。 出现上面的情况时,可能需要以其他方式进行调整(例如,将调整延迟,直至全息影像看不到为止),具体取决于你的应用程序。 RawCoordinateSystem 属性和 RawCoordinateSystemAdjusted 事件会启用这些自定义项。

响应可定位性已更改事件

呈现世界锁定全息影像要求设备在这个世界上进行自身定位。 由于环境条件的原因,该要求并非总能得到满足。如果是这样,用户可能会期望系统在跟踪中断时进行视觉指示。 此视觉指示必须使用附加到设备的参照系(而不是相对于世界来说处于静止状态的参照系)呈现。

应用可以请求系统在跟踪因故中断时进行通知。 注册 LocatabilityChanged 事件,以检测设备在世界中定位自身的能力何时发生变化。 在 AppMain::SetHolographicSpace 中

m_locatabilityChangedToken = m_spatialLocator.LocatabilityChanged(
    std::bind(&HolographicApp6Main::OnLocatabilityChanged, this, _1, _2));

然后,根据此事件确定全息影像何时无法按照相对于世界来说处于静止状态的坐标系呈现。

另请参阅