你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

后期阶段重新投影

后期重投影 (LSR) 是一项硬件功能,用于在用户移动时帮助稳定全息图。

当你在静态模型周围移动时,静态模型在视觉上应保持其位置。 如果它们看起来不稳定,则可能暗示存在 LSR 问题。 请注意,其他动态转换(例如动画或爆炸视图)可能会掩盖此行为。

可以在两种不同的 LSR 模式(即“平面 LSR”或“深度 LSR”)之间进行选择。 尽管两种 LSR 模式有不同的限制,但它们都能提高全息图稳定性。 首先尝试使用深度 LSR,因为在大多数情况下,理论上来说它可以提供更好的结果。

如何设置 LSR 模式

使用哪种 LSR 模式由客户端应用程序是否提交深度缓冲区决定。 如果提交深度缓冲区,则它使用“深度 LSR”和“平面 LSR”,否则不使用

以下段落分别介绍了如何在 Unity 和原生应用程序中完成深度缓冲区的提交。

Unity

在 Unity 编辑器中,转到 。File > Build Settings 选择左下角的 Player Settings,然后在 Player > XR Settings > Virtual Reality SDKs > Windows Mixed Reality 下检查是否选中了 Enable Depth Buffer Sharing

Depth Buffer Sharing Enabled flag

如果已选中,则你的应用将使用深度 LSR,否则将使用平面 LSR。

使用 OpenXR 时,应始终提交深度缓冲区。 可在 中查找设置XR Plug-in Management > OpenXR。 然后,可通过 OpenXR 插件中的扩展更改重新投影模式:

using Microsoft.MixedReality.OpenXR;

public class OverrideReprojection : MonoBehaviour
{
    void OnEnable()
    {
        RenderPipelineManager.endCameraRendering += RenderPipelineManager_endCameraRendering;
    }
    void OnDisable()
    {
        RenderPipelineManager.endCameraRendering -= RenderPipelineManager_endCameraRendering;
    }

    // When using the Universal Render Pipeline, OnPostRender has to be called manually.
    private void RenderPipelineManager_endCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        OnPostRender();
    }

    // Called directly when using Unity's legacy renderer.
    private void OnPostRender()
    {
        ReprojectionSettings reprojectionSettings = default;
        reprojectionSettings.ReprojectionMode = ReprojectionMode.PlanarManual; // Or your favorite reprojection mode.
        
        // In case of PlanarManual you also need to provide a focus point here.
        reprojectionSettings.ReprojectionPlaneOverridePosition = ...;
        reprojectionSettings.ReprojectionPlaneOverrideNormal = ...;
        reprojectionSettings.ReprojectionPlaneOverrideVelocity = ...;

        foreach (ViewConfiguration viewConfiguration in ViewConfiguration.EnabledViewConfigurations)
        {
            if (viewConfiguration.IsActive && viewConfiguration.SupportedReprojectionModes.Contains(reprojectionSettings.ReprojectionMode))
            {
                viewConfiguration.SetReprojectionSettings(reprojectionSettings);
            }
        }
    }
}

原生 C++ 应用程序

提交深度缓冲区完全由原生 C++ 绑定代码控制,独立于 WMR 或 OpenXR 版本。 唯一需要满足的条件是,在调用 GraphicsBinding::BlitRemoteFrame 时,深度缓冲区必须绑定到图形 API。

深度 LSR

要使深度 LSR 正常工作,客户端应用程序必须提供一个有效的深度缓冲区,其中包含要在 LSR 期间考虑的所有相关几何体。

深度 LSR 会尝试根据提供的深度缓冲区的内容来稳定视频帧。 因此,没有呈现给它的内容(例如透明对象)无法通过 LSR 进行调整,可能会显示不稳定和重新投影伪影。

若要降低透明对象的重新投影不稳定性,可以强制执行深度缓冲区写入。 至于有色材料和 PBR 材料,请参阅材料标志 TransparencyWritesDepth。 但请注意,当启用此标志时,透明/不透明对象交互的视觉对象质量可能会受到影响。

平面 LSR

平面 LSR 没有单像素深度信息,而深度 LSR 有此信息。 它会改为根据你必须为每个帧提供的一个平面来重新投影所有内容。

对于靠近所提供平面的对象,平面 LSR 重新投影的效果最佳。 对象离得越远,看起来就越不稳定。 深度 LSR 更适合重新投影不同深度的对象,而平面 LSR 可能更适合与平面对齐的内容。

在 Unity 中配置平面 LSR

平面参数源自所谓的焦点。 使用 WMR 时,必须通过 UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame 设置每一帧的焦点。 有关详细信息,请参阅 Unity 焦点 API。 对于 OpenXR,需要通过上一部分中所示的 ReprojectionSettings 设置焦点。 如果你未设置焦点,系统会为你选择一个回退。 但是,自动回退通常会导致不理想的结果。

你可以自己计算焦点,虽然也可以基于远程渲染主机算出的焦点进行操作。 请调用 RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint 来获取该焦点。

通常情况下,客户端和主机都会呈现另一方不知道的内容,例如客户端上的 UI 元素。 因此,可以将远程焦点与本地算出的焦点结合使用。

在两个连续帧中计算的焦点可能会有很大差异。 直接按原样使用它们可能会导致全息图看起来像是在跳跃。 若要防止此行为,建议在上一个焦点和当前焦点之间进行插值操作。

重投影姿势模式

混合渲染的一般问题范围可以这样表述:远程和本地内容位于不同的姿势(即坐标空间)内,因为远程姿势是由服务器预测的,而本地姿势是实际的当前姿势。 然而,在渲染帧结束时,远程和本地内容都需要对齐并显示在屏幕上。 下图显示了一个示例,其中,与显示屏视区相比,本地和远程姿势被转换:

Diagram that illustrates remote and local pose in relation to target viewport.

根据使用的 GraphicsBinding,ARR 提供最多三种与上述 LSR 模式正交工作的重投影模式。 这些模式称为 Remote pose modeLocal pose modePassthrough pose mode。 与 LSR 模式不同,姿势模式定义了远程和本地内容的组合方式。 选择模式会考虑本地内容的视觉质量,以提升运行时性能,因此应用程序应仔细考虑哪个选项合适。 请参阅下面的注意事项。

Remote pose mode

Remote pose mode 是 ARR 中的默认模式。 在这种模式下,本地内容使用来自远程帧的远程姿势呈现在传入的远程图像流之上。 然后将组合结果转发给操作系统以进行最终重投影。 虽然此方法仅使用一次重投影,但最终校正基于往返间隔,因此完整的重投影误差也适用于本地内容。 因此,大的校正增量可能会导致包括 UI 元素在内的局部几何图形的显著失真。

使用上图,在 Remote pose mode 中应用以下转换:

Reprojection steps in remote pose mode.

Local pose mode

在此模式下,重投影分为两个不同的步骤:第一步,远程内容被重投影到本地姿势空间,即本地内容在 VR/AR 设备上默认渲染的空间。 之后,本地内容使用常用的本地姿势渲染在这个预先转换的图像之上。 第二步,将组合结果转发给操作系统以进行最终重投影。 由于第二次重投影仅产生一个小增量(实际上与不存在 ARR 时将使用的增量相同),本地内容上的失真伪影得到了显著减轻。

因此,此图如下所示:

Reprojection steps in local pose mode.

Passthrough pose mode

此姿势模式的行为本质上与 Remote pose mode 相同,这意味着本地和远程内容在远程空间中组合。 但是,内容在组合后不会重新投影,而是保留在远程姿势空间中。 此模式的主要优点是,生成的图像不会受到重新投影伪影的影响。

从概念上讲,此模式可以与传统的云流式处理应用程序相比较。 由于其产生的延迟较高,因此不适合头戴式方案,但对于需要更高的图像质量的桌面和其他平板应用程序来说,它是一种可行的替代方法。 因此,它目前仅可用于 GraphicsBindingSimD3D11

性能和质量注意事项

姿势模式的选择对视觉质量和性能有影响。 在 HoloLens 2 设备上在 Local pose mode 中进行额外重投影的客户端的额外运行时成本约为每帧 GPU 时间 1 毫秒。 如果客户端应用程序已经接近 16 毫秒的帧预算,则需要考虑这一额外成本。 另一方面,有些类型的应用程序要么没有本地内容,要么本地内容不容易失真。 在这种情况下,Local pose mode 不会获得任何视觉优势,因为远程内容重投影的质量不受影响。

因此,一般建议是在每个用例的基础上测试模式,并查看视觉质量的提高是否证明额外的性能开销是合理的。 也可以动态切换模式,例如仅在显示重要 UI 时启用本地模式。

如何在运行时更改 Pose mode

可使用以下客户端 API 在运行时更改模式:

RenderingSession session = ...;
session.GraphicsBinding.SetPoseMode(PoseMode.Local); // set local pose mode
ApiHandle<RenderingSession> session = ...;
session->GetGraphicsBinding()->SetPoseMode(PoseMode::Local); // set local pose mode

通常情况下,只要图形绑定对象可用,就可以更改模式。 对于 GraphicsBindingSimD3D11 而言,有一个重要区别:如果已使用代理纹理初始化姿势模式,只可将其更改为 PoseMode.Remote。 如果不是这种情况,则在图形绑定重新初始化之前只能在 PoseMode.LocalPoseMode.Passthrough 之间切换姿势模式。 查看两个 GraphicsBindingSimD3d11.InitSimulation 重载,这两者分别采用指向 ID3D11Texture2D 对象(代理路径)的本机指针和指向预期用户视区(非代理路径)的 widthheight 本机指针。

桌面端 Unity 运行时注意事项

鉴于 GraphicsBindingSimD3D11 技术背景和使用 Unity 实现的屏幕外呈现效果,ARR Unity 运行时要求用户在启动 RemoteManagerUnity 时指定所需的姿势模式,如下所示:

public static void InitRemoteManager(Camera camera)
{
    RemoteUnityClientInit clientInit = new RemoteUnityClientInit(camera, PoseMode.Remote);
    RemoteManagerUnity.InitializeManager(clientInit);
}

如果已指定 PoseMode.Remote,则将使用屏幕外代理纹理初始化图形绑定,并将所有渲染从 Unity 场景的主相机重定向到代理照相机。 仅在需要运行时姿势模式更改为 PoseMode.Remote 时,才建议使用此代码路径。 如果未指定姿势模式,ARR Unity 运行时将根据当前平台选择适当的默认值。

警告

代理照相机重定向可能与其他 Unity 扩展不兼容,这种情况下,需要使用主相机进行场景渲染。 如果需要在其他位置查询或注册代理摄像机,可以通过 RemoteManagerUnity.ProxyCamera 属性加以检索。 具体而言,对于 Cinemachine 插件,请参考此故障排除条目:Unity Cinemachine 插件在远程姿势模式下不起作用

如果改为使用 PoseMode.LocalPoseMode.Passthrough,则将不会使用屏幕外代理纹理对图形绑定进行初始化,并且将使用采用 Unity 场景主相机的快速路径。 如果相应的用例在运行时需要远程姿势模式,则应在 RemoteManagerUnity 初始化时指定 PoseMode.Remote。 使用 Unity 的主相机直接渲染效率更高,并可以防止其他 Unity 扩展出现问题。 因此,建议使用非代理渲染路径。

后续步骤