版本:Unity 6 (6000.0)
语言:English
XR SDK 输入子系统
XR SDK 网格子系统

XR SDK 显示子系统

XR一个涵盖虚拟现实 (VR)、增强现实 (AR) 和混合现实 (MR) 应用程序的总称。支持这些交互式应用程序的设备可以称为 XR 设备。更多信息
参见 术语表
SDK 显示子系统提供了一个用于纹理分配、帧生命周期和阻塞以实现节奏的接口。

纹理分配

一些设备 SDK 要求通过 SDK 本身而不是通常的图形 API 来分配纹理。如果您使用 XR SDK 显示子系统,则不再需要依赖外部插件在 Unity 之外创建的一组代码,用于在 Unity 中创建功能。您可以在 Unity 中使用两种类型的插件:托管插件(使用 Visual Studio 等工具创建的托管 .NET 程序集)和原生插件(特定于平台的原生代码库)。更多信息
参见 术语表
blit“位块传输”的简称。blit 操作是将内存中的一块数据传输到另一块内存的过程。
参见 术语表
或复制到 SDK 纹理中。

显示子系统使插件提供程序能够分配纹理。在可能的情况下,Unity 会直接渲染到纹理以避免不必要的复制。如果需要,Unity 也可以为您分配纹理。

在以下情况下,Unity 无法直接渲染到纹理,而是渲染到中间纹理,然后 blit 或复制到您的纹理

  • 如果使用了图像效果,您的纹理将位于链的末尾。
  • 在使用 MSAA 的 PC 上,Unity 会渲染到多采样纹理,然后在摄像机一个组件,用于创建场景中特定视点的图像。输出要么绘制到屏幕上,要么作为纹理捕获。更多信息
    参见 术语表
    堆栈的末尾将其解析到您的纹理中。
  • 在移动设备上,使用多采样自动解析扩展时,当纹理被刷新或用作除绘制到它之外的任何操作的源或目标时,纹理将被隐式解析。请参阅 EXT_multisampled_render_to_texture 扩展。
  • 延迟渲染、HDR高动态范围
    参见 术语表
    和命令缓冲区会导致 Unity 渲染到中间纹理。
  • 如果渲染到屏幕的子集,Unity 会渲染到中间纹理。
  • 如果“纹理布局”与 Unity 正在渲染到的内容不匹配(例如,Unity 将每个眼睛渲染到单独的纹理,但 RenderPass 配置为渲染到纹理数组或单个纹理)。
  • 如果设置了 kUnityXRRenderTextureFlagsLockedWidthHeight 标志并且 renderScale 不为 1.0。
  • 如果设置了 kUnityXRRenderTextureFlagsWriteOnly 标志并且 Unity 需要从纹理中读取回数据。

MSAA

在 PC 和移动设备上,引擎始终解析到提供程序的纹理。引擎执行隐式解析(在具有多采样渲染到纹理扩展的移动设备上)或显式解析。

在移动设备上,提供程序应启用 kUnityXRRenderTextureFlagsAutoResolve 标志并使用 1 个样本创建其纹理。

sRGB

使用 UnityXRFrameSetupHints.appSetup.sRGB 检查 Unity 是否期望渲染到 sRGB 纹理格式。提供程序最终从 UnityXRRenderTextureDesccolorFormat 字段中选择输出纹理格式用于处理 3D 图形硬件(如显卡或移动设备)实时渲染期间的纹理的文件格式。更多信息
参见 术语表
。如果格式为 sRGB 类型,则 Unity 会根据活动项目选择的颜色空间打开或关闭 sRGB 写入。您应该始终使用 sRGB 到线性转换在合成器中从任何 sRGB 纹理中采样。

深度纹理

如果您的 SDK 需要深度信息,您可以像上面颜色缓冲区一样获取深度缓冲区一个内存存储,用于保存图像中每个像素的 z 值深度,其中 z 值是从投影平面到每个渲染像素的深度。更多信息
参见 术语表
UnityXRRenderTextureDesc 上的 nativeDepthTex 值指定了原生资源。默认情况下,如果 nativeDepthTex 设置为 kUnityXRRenderTextureIdDontCare,Unity 会尝试在具有类似描述的纹理之间共享深度缓冲区。

如果您的 SDK 不需要深度信息,则应将 UnityXRRenderTextureDesc::depthFormat 设置为 kUnityXRDepthTextureFormatNone 以避免不必要的解析。

处理双缓冲和三缓冲

在提交期间(请参阅下面的提交正在进行的帧部分),您可以为每一帧指定不同的纹理 ID,以处理 SDK 需要双缓冲或三缓冲 Unity 正在渲染到的图像的情况。提供程序插件负责管理 UnityXRRenderTextureId 的集合。

帧生命周期

有两种方法负责帧的生命周期:PopulateNextFrameDesc,它发生在渲染开始之前,以及 SubmitCurrentFrame,它发生在渲染完成后立即。这两种方法都在图形线程上调用。

PopulateNextFrameDesc 期间,预计显示提供程序将执行以下操作

  • 等待节奏(等待 Unity 应该再次开始提交渲染命令)。或者,这可以在 SubmitCurrentFrame 中完成。
  • 获取下一张图像并通过 nextFrame 参数告诉 Unity 下一次渲染到哪些纹理 ID。

SubmitCurrentFrame 方法期间,预计显示提供程序将执行以下操作

  • 提交正在进行的最后一帧(例如,眼睛纹理或深度纹理)以在屏幕上显示。
  • 等待节奏,作为在 PopulateNextFrameDesc 期间等待的替代方案。

阻塞以实现节奏

为了在渲染到 HMD 显示器时保持尽可能低的延迟和最大的吞吐量,您需要确保在获取姿势和提交纹理时进行精确的计时。每个 HMD 都有一个其合成器运行的原生刷新率。比该速率快地渲染会导致次优体验,因为计时不匹配或工作冗余。

Unity 期望显示提供程序在帧生命周期期间阻塞或等待帧节奏。Unity 在从阻塞调用“唤醒”后不久开始提交渲染命令。您应该将唤醒时间与特定窗口内的合成器同步。一些 SDK 提供了一个基于启发式的浮动唤醒时间窗口。Meta/Oculus 将其称为“提前排队”(有关更多信息,请参阅Oculus 开发人员文档)。Valve 将其称为“运行开始”(请参阅此演示文稿的幻灯片 18 和 19)。

Unity 等待帧生命周期完成,然后才开始提交依赖于姿势的图形命令。

在哪里等待节奏

提供程序可以在 PopulateNextFrameDescSubmitCurrentFrame 中等待节奏。

当 Unity 在图形线程上提交一帧的图形命令时,下一帧的模拟循环在主线程上运行。它包含物理、脚本逻辑等。PopulateNextFrameDesc 在所有渲染命令都提交后,并且仅在下一帧的模拟以及在其上计划的所有图形作业完成后,在图形线程上被调用。PopulateNextFrameDesc 等待的一个图形作业是当前帧的 SubmitCurrentFrame。这就是为什么在 SubmitCurrentFrame 中等待节奏是有效的。此外,Unity 只有在 PopulateNextFrameDesc 完成后才会开始渲染。

考虑到这些细节,在 SubmitCurrentFrame 中等待节奏与在 PopulateNextFrameDesc 中等待节奏相比,存在一些权衡。例如,如果应用程序在模拟期间安排了昂贵的图形作业,则在 SubmitCurrentFrame 中等待节奏可能会导致性能问题。因为 SubmitCurrentFrame 计划在渲染之后运行,所以应用程序计划的图形作业将在 SubmitCurrentFrame 之后但在 PopulateNextFrameDesc 之前运行。在这种情况下,提供程序正在 SubmitCurrentFrame 中等待,然后它唤醒并期望 Unity 开始渲染。但是,Unity 在调用 PopulateNextFrameDesc 之前处理应用程序计划的图形作业,这反过来又允许 Unity 开始渲染。唤醒以进行渲染和处理在更新方法中计划的图形作业之间的延迟可能会导致延迟。开发人员可以通过安排其图形作业在渲染之后来优化这一点,以确保图形作业在 SubmitCurrentFrame 之前安排。

虽然提供程序在 SubmitCurrentFrame 中等待节奏允许计算图形作业与主线程并行运行,但在 PopulateNextFrameDesc 中等待节奏会完全阻塞 Unity 主线程。这是可以接受的,因为模拟和其他图形作业已经完成。当模拟或图形线程占用过多的时间并超过设备的目标帧率时,可能会出现问题。这会导致帧率减半,而 PopulateNextFrameDesc 等待节奏中的下一个周期。

提交正在进行的帧

当 Unity 调用 SubmitCurrentFrame 时,您在上一次帧中设置的纹理已经渲染到,或者 Unity 已将渲染命令提交到图形驱动程序以渲染它们。Unity 现在已经完成了这些操作,您可以将它们传递给您的合成器。

填写下一帧描述符

在阻塞或获取要渲染的下一帧后,您必须告诉 Unity 下一帧要渲染哪些纹理,以及渲染通道的布局是什么(请参阅下面的渲染通道)。

渲染通道

一个UnityXRRenderPass可能包含一个剔除传递和一个场景场景包含游戏中的环境和菜单。可以将每个唯一的场景文件视为一个独特的关卡。在每个场景中,您放置环境、障碍物和装饰,从本质上讲,将游戏分段设计和构建。 更多信息
参见 术语表
图遍历。这是一个资源密集型操作,您应该尝试通过诸如单通道渲染之类的技巧来限制 Unity 执行它的次数。

每个UnityXRRenderPass包含一个输出纹理(可以是纹理数组)和输出UnityXRRenderParams,例如视图、投影矩阵以及要渲染到的矩形或纹理数组切片。

对于每一帧,显示提供程序都会设置一个UnityXRRenderPass并填写 Unity 将渲染到下一帧的UnityXRRenderTextureId

UnityXRRenderPass的用例包括以下内容

  • 双通道立体渲染(2 个 RenderPass x 1 个 RenderParams)
  • 单通道立体渲染(1 个 RenderPass x 2 个 RenderParams)

API 支持以下其他情况(但 Unity 目前可能无法正确响应)

  • 四边形一个类似于平面的基本对象,但其边长仅为一个单位,它仅使用 4 个顶点,并且表面位于局部坐标空间的 XY 平面中。 更多信息
    参见 术语表
    传递宽 FOV 立体渲染(4 个 RenderPass x 1 个 RenderParams)
  • 单通道 + 宽 FOV 立体渲染(1 个 RenderPass x 2 个 RenderParams + 2 个 RenderPass x 1 个 RenderParams)
  • 凹陷渲染
    • 每只眼睛两个单独的纹理(内侧和外侧)
    • 一个纹理,UV 贴合
  • 外部视图场景(HoloLens BEV,Mayo 第三只眼)(额外的 RenderPass)
  • 用于合成场景中对象或人物的近/远渲染(2 个 RenderPass,不同的投影,不同的目标)

可以安全地做出以下假设

  • 每个传递一个纹理(单通道是一个纹理数组)
  • 如果为单通道,则每个传递两个姿势/投影(或凹陷渲染)

注意:Unity 项目和 XR SDK 必须对单通道渲染使用相同的设置(启用/禁用),因为此设置会影响用户着色器在 GPU 上运行的程序。 更多信息
参见 术语表
。要检查是否启用了单通道渲染,请使用UnityXRFrameSetupHints.appSetup.singlePassRendering

剔除传递

如果两个渲染传递的cullingPassIndex设置为相同的值,则它们可以共享一个剔除传递。cullingPassIndex选择要使用的UnityXRCullingPass。必须在UnityXRNextFrameDesc中填写剔除传递。

XR SDK 输入子系统
XR SDK 网格子系统