注视点渲染是一种优化技术,可使位于用户视野外围的显示器区域以较低的分辨率渲染。注视点渲染可以提高渲染性能,而对用户感知的视觉质量几乎没有影响。
注视点 (foveated) 渲染的名称源自“中央凹”(fovea) 一词,这是眼睛中一块很小的区域,包含最密集的光感神经。注视点渲染的目标是让屏幕上不同区域采用不同的渲染分辨率,以便只有中央凹可以感知的部分才以最高分辨率渲染。
XR 平台使用各种技术(例如可变速率着色 (VRS) 和可变速率光栅化 (VRR))实现注视点渲染。使用的技术可以更改在注视点渲染被激活时着色器必须对纹理进行采样的方式。为了让资源能够在任何平台上工作,Unity 提供了着色器宏、关键字和预处理器符号,可用于调整着色器,以便屏幕空间计算在所有平台上产生正确的结果。请参阅注视点渲染着色器以了解如何使用它们。(Unity 和 URP 提供的着色器已经开始使用这些宏。)
XR 设备可以在两种模式之一中支持注视点渲染:
目前,使用__ VRS__虚拟现实。更多信息
See in Glossary 和均匀光栅进行注视点渲染的__ XR__虚拟现实(VR)、增强现实(AR)和混合现实(MR)应用的泛指术语。支持这些形式的交互式应用程序的设备可被称为 XR 设备。更多信息
See in Glossary 平台包括 OpenXR 和 Meta Quest。使用 VRR 和非均匀光栅进行注视点渲染的 XR 平台包括 visionOS(使用 Metal 图形 API 进行渲染时)。
要使用注视点渲染,项目必须满足以下先决条件:
注意:
要为 Unity 项目启用注视点渲染,请转到项目设置 (Project Settings) 的 XR 插件管理 (XR Plug-in Management) 部分(菜单:编辑 (Edit) > 项目设置 (Project Settings))。在每个支持注视点渲染的插件的设置部分,都会出现启用该功能的选项。请参阅 XR 提供程序插件的文档以了解有关可用设置的信息。
如控制注视点渲染中所述,除了在设置中启用该选项之外,还必须在运行时设置注视点渲染级别。
注意:XR 平台可提供不同级别的注视点渲染控制。例如,在 visionOS 上使用 Metal 渲染时,启用固定注视点渲染后,它始终处于全强度。
通过将 XRDisplaySubsystem 的 foveatedRenderingLevel 属性设置为 0 到 1 之间的值来控制注视点渲染。必须在项目的提供程序插件设置中启用注视点运行时,否则运行时设置将被忽略。
以下示例方法展示了如何设置注视聚焦级别:
public void SetFRLevel(XRDisplaySubsystem xrDisplaySubsystem, float strength)
{
xrDisplaySubsystem.foveatedRenderingLevel = strength;
}
如果将注视点渲染级别设置为零,则注视聚焦会关闭,但不会影响渲染。
在支持眼睛跟踪或凝视式注视点渲染的设备上,可以通过设置 XRDisplaySubsystem 的 foveatedRenderingFlags 属性来启用该功能:
// xrDisplaySubsystem is the active XRDisplaySystem instance
xrDisplaySubsystem.foveatedRenderingFlags = XRDisplaySubsystem.FoveatedRenderingFlags.GazeAllowed;
即使在运行时选择了固定注视点渲染,也可以通过在支持 foveatedRenderingFlags 的设备上设置其属性来启用凝视式注视点渲染。
注意:从 SubsystemManager 获取 XRDisplaySubsystem:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class FoveationControl : MonoBehaviour
{
XRDisplaySubsystem xrDisplaySubsystem;
public float strength = 1.0f;
void Start()
{
// Find the XR display subsystem
var xrDisplaySubsystems = new List<XRDisplaySubsystem>();
SubsystemManager.GetSubsystems<XRDisplaySubsystem>(xrDisplaySubsystems);
if (xrDisplaySubsystems.Count < 1)
{
Debug.LogError("No XR display subsystems found.");
return;
}
foreach(var subsystem in xrDisplaySubsystems)
{
if (subsystem.running)
{
xrDisplaySubsystem = subsystem;
break;
}
}
xrDisplaySubsystem.foveatedRenderingFlags = XRDisplaySubsystem.FoveatedRenderingFlags.GazeAllowed;
SetFRLevel();
}
public void SetFRLevel()
{
xrDisplaySubsystem.foveatedRenderingLevel = strength;
}
}
可以从 SystemInfo 对象的 foveatedRenderingCaps 属性获取当前设备的注视点渲染功能。
FoveatedRenderingCaps caps = SystemInfo.foveatedRenderingCaps;
如果属性报告 FoveatedRenderingCaps.None,则设备不支持注视点渲染。缺少支持可能是由各种原因引起的,包括软件、设备运行时或驱动程序,因此即使平台支持注视点渲染,特定设备上也可能不支持此功能。
FoveatedRenderingCaps 的其他值(FoveationImage 和 NonUniformRaster)提供了有关设备实现注视点渲染的方式的信息,如果要实现自定义渲染管线,这些信息可能会很有用。FoveationImage 表示注视点渲染实现使用 VRS,而 NonUniformRaster 表示其使用 VRR。
Unity 引擎附带的着色器已经能够支持均匀和非均匀光栅化,可用于注视点渲染。如果有执行屏幕空间计算的自定义着色器,则可能需要更新这些着色器以支持使用非均匀光栅技术的平台上的注视点渲染。
使用非均匀光栅渲染的像素不再代表常规网格,因此必须更改任何假定使用常规网格的屏幕空间计算,才能让这些像素正确使用 VRR。Unity 提供了着色器宏、关键字和预处理器符号,可用于调整着色器,以便屏幕空间计算在所有平台上产生正确的结果。(Unity 和 URP 提供的着色器已经开始使用这些宏。)
例如,下图的左侧显示了使用 VRR 渲染但未校正非均匀光栅化的场景视图。正确渲染后,图像的右侧显示相同的视图:
请注意左侧图像边缘附近的区域是如何被挤压的。发生这种视觉压缩的原因是纹理的外围区域以较低的像素密度进行了光栅化。对此纹理进行采样时,需要使用非均匀 UV 坐标(或使用本手册提供的注视点渲染着色器函数之一将线性 UV 坐标转换为非均匀坐标)。
注意:Unity 使用术语“非均匀光栅”来描述使用 VRR 技术时像素被光栅化的空间,并使用术语“线性光栅”来表示具有常规网格的更常见的光栅化空间。
要更新 URP 的屏幕空间着色器以便在注视点渲染下产生正确的结果,请添加以下 include 文件(来自 Core RP Library 包):
#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
这些文件包含用于注视点渲染的关键字和宏,以及用于将屏幕空间坐标从非均匀坐标映射到线性坐标或从线性坐标映射到非均匀坐标的函数。
可变速率着色 (VRS) 技术根据位于用户视野中的区域更改每次调用片元着色器时所着色的屏幕像素数量。可变速率着色与现有着色器兼容。在使用 VRS 的 XR 平台上,应该无需更改自定义着色器即可在注视点渲染下工作。
可变速率光栅化 (VRR) 技术可以调整光栅化,使相邻像素之间的有效距离不再均匀。例如,视野的外围区域可能会以较低分辨率渲染到纹理上,然后在合成到最终显示画面时拉伸该纹理。由于光栅化是非均匀的,因此如果在屏幕空间中执行计算的着色器假设光栅化使用均匀网格,可能会在 VRR 下产生不正确的结果。在使用 VRR 的 XR 平台上,可能需要更改自定义着色器才能在注视点渲染下工作。
对线性纹理进行采样时,应使用线性坐标。相反,对非均匀纹理进行采样时,应使用非均匀坐标。
从顶点着色器传递到片元(像素)着色器的所有顶点属性始终位于线性空间中,但 SV_POSITION 除外。可以直接使用 SV_POSITION 坐标对非均匀纹理进行采样,但如果使用其他顶点属性进行采样,则必须使用适当的重新映射函数完成线性到非均匀的转换。同样,如果使用 SV_POSITION 对线性纹理进行采样,则必须使用 FoveatedRemapNonUniformToLinear 函数。
纹理的坐标空间因其创建方式而异。使用 VRR 渲染时,Unity 引擎创建的大多数纹理(例如深度缓冲区和 G 缓冲区)都位于非均匀空间中。(但是,有一些例外情况,目前还没有记录在案。)手动创建的屏幕空间纹理(例如美术师绘制的__ UI__(即用户界面,User Interface)让用户能够与您的应用程序进行交互。Unity 目前支持三种 UI 系统。更多信息
See in Glossary 覆盖层)通常是线性的,因此应该使用线性 UV 坐标对它们进行采样。
在计算着色器中,必须考虑源纹理和目标纹理是在线性空间还是非均匀空间中,并在采样或写入之前根据需要重新映射坐标。同样,请考虑 UV 坐标的来源是什么、如何在计算着色器中计算这些坐标,以及传递这些坐标的任何函数是需要线性坐标还是非均匀坐标。请参阅计算着色器主题和 ComputeShader API,了解有关 Unity 中计算着色器的一般信息。
FoveatedRendering.hlsl 定义了一组函数,可用于支持自定义着色器中的注视点渲染。无论当前注视点渲染类型是线性还是非均匀,以及在不使用注视点渲染时,这些函数都会返回正确结果。这些函数还根据平台将屏幕空间原点放置在屏幕顶部还是底部来处理 y 轴的反转。
注意:这些函数对不使用非均匀注视点渲染 (VRR) 的平台没有性能影响。无需使用条件 #ifdef 语句来排除它们。但是,在使用 VRR 的平台上,有一个动态分支可以检查着色器常量,因此即使禁用了注视点渲染,也会增加少量开销。
声明:float2 FoveatedRemapLinearToNonUniform(float2 uv)
将线性屏幕空间 UV 坐标转换为非均匀坐标,包括任何必要的 y 轴反转。如果注视点渲染未激活或平台未使用非均匀光栅化,则函数返回不变的 UV 坐标。
声明:float2 FoveatedRemapNonUniformToLinear(float2 uv)
将非均匀屏幕空间 UV 坐标转换为线性坐标,包括任何必要的 y 轴反转。如果注视点渲染未激活或平台未使用非均匀光栅化,则函数返回不变的 UV 坐标。
声明:float2 FoveatedRemapPrevFrameLinearToNonUniform(float2 uv)
根据先前渲染的帧重新映射线性屏幕空间 UV 坐标。(未在 Metal 上实现。)
声明:float2 FoveatedRemapPrevFrameNonUniformToLinear(float2 uv)
根据先前渲染的帧重新映射非均匀屏幕空间 UV 坐标。(未在 Metal 上实现。)
声明:float2 FoveatedRemapDensity(float2 uv)
指定屏幕 UV 坐标处的屏幕分辨率和光栅分辨率之间的比率。如果注视点渲染未激活或平台不使用非均匀光栅化,则函数返回 (1.0, 1.0)。(未在 Metal 上实现。)
声明:float2 FoveatedRemapPrevFrameDensity(float2 uv)
前一帧时的密度比。(未在 Metal 上实现。)
声明:float2 FoveatedRemapLinearToNonUniformCS(float2 positionCS)
将线性空间屏幕坐标转换为非均匀空间屏幕坐标。如果注视点渲染未激活或平台未使用非均匀光栅化,则函数返回不变的坐标。
声明:float2 FoveatedRemapNonUniformToLinearCS(float2 positionCS)
将非均匀空间屏幕坐标转换为线性空间屏幕坐标。如果注视点渲染未激活或平台未使用非均匀光栅化,则函数返回不变的坐标。
要将线性屏幕空间中的 UV 坐标转换为非均匀光栅屏幕空间中的坐标,请使用以下内容:
uvNonUniform = FoveatedRemapLinearToNonUniform(uvLinear);
要将非均匀光栅屏幕空间中的 UV 坐标转换为线性屏幕空间中的坐标,请使用以下内容:
uvLinear = FoveatedRemapNonUniformToLinear(uvNonUniform);
要更新特效,例如高斯模糊,请使用以下内容:
uvNonUniform = FoveatedRemapDensity(uvLinear);
Shader Graph 中的 Screen Position 节点提供的坐标通常用于对受注视点渲染影响的纹理进行采样。此 Shader Graph 节点的模式 (Mode) 设置决定了提供坐标的形式。在大多数模式下,使用 VRR 渲染时的坐标空间是不均匀的。相比之下,Raw 模式在所有情况下都是线性的。
| Screen Position 模式 | UV 坐标空间 || | | 可变速率着色 (VRS) 和非注视点渲染 | 可变速率光栅化 (VRR) | | :——– | :—-: | :———: | Default | 线性 | 非均匀 Raw | 线性 | 线性 Center | 线性 | 非均匀 Tiled | 线性 | 非均匀 Pixel | 线性 | 非均匀
如果使用 Screen Position 节点的 Raw 模式对非均匀纹理进行采样,则结果将不正确。
为了说明这种情况,假设有一个 Shader Graph,其中 Screen Position 节点与 Scene Depth 节点连接,而 Scene Depth 节点又与片元着色器的基色连接。
此着色器根据场景深度缓冲区中的深度值渲染颜色。此着色器在注视点渲染下正常工作,因为它使用 Default 模式 Screen Position 节点,该节点返回的结果与深度缓冲区(以及大多数其他引擎创建的纹理)位于同一空间中。但是,如果将模式更改为 Raw,则在使用可变速率光栅化的平台上激活注视点渲染时,结果将不再正确。
| 正确采样与不匹配采样 | |
|---|---|
![]() |
![]() |
| 左图显示了四边形上运行的着色器,它使用正确的坐标空间对场景深度缓冲区进行采样。 | 右图显示了对采样纹理使用错误坐标空间的注视点渲染,请注意采样纹理如何不再与场景几何体对齐。 |
如果您的 Shader Graph 依赖于 Raw Screen Position 并且无法使用任何其他模式,则可以在 Custom Function 节点中使用 Shader 函数进行注视点渲染以重新映射原始坐标(必须对其进行归一化处理):
#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRendering.hlsl"
// The Raw mode screenPosition must first be normalized: position.xy/position.ww
void CorrectedScreenPosition_float(float4 screenPosition, out float2 correctedScreenPosition)
{
correctedScreenPosition = FoveatedRemapLinearToNonUniform(screenPosition);
}
注意:在这种情况下,必须创建 Custom Function 节点来引用文件,因为如果使用 String Type,#include 语句将位于函数体内部并导致语法错误。
例如,以下 Shader Graph 通过两个 Custom Function 节点路由 Raw 坐标:
第一个自定义函数通过将 x 和 y 元素除以 w 元素 (B = A.xy/A.ww) 来归一化原始坐标。第二个自定义函数使用 FoveatedRemapLinearToNonUniform 在需要时将线性坐标映射到非均匀空间中。请记住,当注视点渲染关闭或当前平台不使用 VRR 进行注视点渲染时,FoveatedRemapLinearToNonUniform 会返回不变的线性坐标。因此,着色器在所有情况下都能正确采样场景深度。