FidelityFX 景深 1.1

目录
引言
FidelityFX 景深(简称 DoF)是一种旨在重现相机镜头产生的散景景深效果的技术。
着色语言和 API 要求
DirectX 12 + HLSL
HLSLCS_6_2CS_6_6†
† 在某些支持 64 位宽波前的硬件上使用 CS_6_6。
Vulkan + GLSL
-
Vulkan 1.x
-
GLSL 4.50,并支持以下扩展GL_EXT_samplerless_texture_functionsGL_EXT_control_flow_attributes(可选)GL_EXT_shader_subgroup_extended_types_float16GL_EXT_shader_16bit_storageGL_EXT_shader_explicit_arithmetic_typesGL_KHR_shader_subgroup_basicGL_KHR_shader_subgroup_voteGL_KHR_shader_subgroup_arithmeticGL_KHR_shader_subgroup_ballotGL_KHR_shader_subgroup_quadGL_KHR_shader_subgroup_shuffle
请注意,GLSL 编译器还必须支持 GL_GOOGLE_include_directive 以处理 GLSL 着色器系统中使用的 #include。
集成指南
输入资源
所需输入的唯一数据是帧颜色缓冲区和每像素弥散圆(CoC)半径,通常由深度缓冲区生成。
由于该技术在半分辨率(或四分之一面积分辨率)缓冲区上运行,因此必须先对输入进行降采样。提供多个 MIP 级别也可以提高在大弥散圆半径区域的性能和质量。推荐使用 FidelityFX 单通道降采样器,源代码中包含了一个用于双边缩减的辅助函数。
此外,还需要每个瓦片的最小和最大 CoC 值,并且在源代码中包含了一个用于计算这些值的着色器。
在使用 SDK 上下文时,只需要在 FfxDofDispatchDescription 中提供当前帧的颜色和深度缓冲区。CoC 半径是从深度缓冲区计算的。降采样作为效果的一部分运行。
计算弥散圆半径
为了获得逼真的景深效果,推荐使用薄透镜模型。在该模型中,CoC 半径由以下公式计算:
CocRadius = abs( (ApertureRadius * FocalLength * (FocusedDistance - ObjectDistance)) / (ObjectDistance * (FocusedDistance - FocalLength)) )通过将 z 缓冲区值转换为视图空间深度的函数替换为物体距离,并简化所有不变变量,我们得到一个形式为 CocRadius = abs(z * CocScale + CocBias) 的函数。缩放和平移项应在 FfxDofDispatchDescription 中提供。
为了从投影矩阵和镜头模型参数计算这些值,包含了一些辅助函数(ffxDofCalculateCocScale/Bias)。
重要提示:半径的符号用于区分近场和远场。远场像素使用负号,近场使用正号。如果相机指向负 Z 方向,则上述计算中的焦点距离也应为负值。
输入的半径单位为半分辨率像素。
为了性能考虑,限制半径值可能会有益,例如通过钳位(注意,限制应随输入分辨率进行缩放,以使输出在不同分辨率下看起来相同)。
可以使用 FfxDofContextDescription 中的 cocLimitFactor 字段来实现此目的。它是一个乘以输入/输出图像高度的因子。
此机制还可以用于将效果限制在仅远场或仅近场,尽管 SDK 未公开单独的近场和远场限制。
质量设置
有几个设置有助于提高质量。最重要的是,内核样本数量通过 FfxDofContextDescription 中的 quality 参数间接控制,该参数控制样本环的数量。
样本数量与此数量呈渐近二次关系。请注意,实际样本环的数量也取决于内核半径;该参数是上限。此选项在着色器代码中也作为 FFX_DOF_OPTION_MAX_RINGS 提供。
在着色器代码中,FFX_DOF_OPTION_MAX_MIP 常量决定了采样的最大 MIP 级别。它应设置为可用的最大 MIP 级别。默认使用第一个 MIP,如果环的数量小于内核半径(即每像素样本少于一个),则使用更高的 MIP。注意:此选项未在 SDK 中公开,SDK 始终使用 4 个 MIP 级别。
标志 FFX_DOF_DISABLE_RING_MERGE 通过着色器选项 FFX_DOF_OPTION_MAX_RING_MERGE_LOG 控制环合并。该设置是平坦区域中要合并的最大环数的二的对数。
它被设置为 1 或 0,基于 FFX_DOF_DISABLE_RING_MERGE,因为更高的值可能导致可见的块状伪影。
反转深度缓冲区
对于 CoC 瓦片化通道,如果深度缓冲区是反转的,则生成的 CoC 值必须交换。只需在创建上下文时设置 FFX_DOF_REVERSE_DEPTH 标志即可处理此问题,或者在包含 ffx_dof_downsample_depth.h 头文件之前定义 #define FFX_DOF_OPTION_REVERSE_DEPTH 1。
此设置不影响任何其他通道。特别是,深度值从不直接使用;它们始终转换为 CoC 半径。
时间稳定性
模糊效果会放大输入中的任何时间不稳定性。因此,建议传递一个时间稳定的图像,即在帧中将 DoF 放在 TAA 之后。请注意,对于某些 TAA 实现,这可能会导致边缘将颜色泄露到附近的像素。
向上采样
当使用景深和渲染放大时,应在渲染分辨率下应用 DoF,即在放大之前。这是因为需要每像素 CoC 信息,而 CoC 信息通常来自深度缓冲区。
深度通常仅在渲染分辨率下可用,并且不会被放大。
技术
双边降采样
降采样使用双边核生成 MIP 贴图。2x2 像素块中每个像素的权重根据与左上角像素的 CoC 半径的绝对差来计算。
如果所有像素具有相同的 CoC 半径,则此操作等同于简单的平均。如果差值大于一个(像素半径),则该像素的权重为零。
MIP 贴图链的所有级别都使用相同的核。
瓦片图创建
此通道获取每个瓦片的最小和最大半径。瓦片中的全局最大半径也存储起来,为膨胀通道做准备。

瓦片图膨胀

全局最大半径在膨胀通道中用作内核大小。
膨胀函数使用散点收集(scatter-as-gather)算法。近场和远场半径会影响中心瓦片中的任何像素。检查所有范围内的瓦片。
对于每个瓦片,会检查该瓦片的扩散半径与中心的距离。如果距离更小,意味着该瓦片中的某些像素可能会扩散到中心瓦片中的至少一个像素,则会更新收集的半径。
瓦片分类
在模糊通道开始时,每个瓦片根据其膨胀的近场和远场 CoC 值进行分类。如果半径小于半个半分辨率像素,则该瓦片仅被复制到远场输出。
如果整个瓦片都在近场或远场,则会跳过不相关的代码段。如果近场和远场半径之间的差值很小,则会运行一个简化版本,其中所有样本权重都相等。
近场模糊

近场中的所有样本都使用加权平均值进行累加,使用散点收集算法。超出范围的样本将被丢弃。权重是根据 CoC 半径的归一化倒数计算的。
近场颜色的不透明度由附近背景样本的数量确定,并存储在近场输出的 alpha 通道中。
孔洞填充
对于近场中的物体,边缘应该均匀地模糊到轮廓的内侧和外侧,以保持其可见尺寸。重现这种效果需要来自物体后方的颜色样本,由于遮挡,这些样本在颜色缓冲区中是不可用的。作为一种近似,这些样本会从周围区域进行外插。
具体来说,如果中心样本在近场,则任何背景样本(无论其范围或遮挡如何)都将累加为“填充”颜色,并在最后添加到远场颜色中。
远场模糊

对于远场,样本按照 Abadie 2018 的建议,以环状从外向内收集。这近似了正确的遮挡,因为内部样本倾向于更靠近相机。
环桶收集
对于每个环,样本会累加到两个桶中:一个用于当前环,一个用于所有先前环。每个环之后,当前桶会合并到先前桶中。
对于颜色,使用加权求和,使用与近场模糊相同的权重计算。
此外,我们还平均了样本的(归一化)半径。合并时,桶被近似为环,并使用重叠来遮挡先前桶。
环合并
在平坦区域,所有权重都被假定为相等。因此,我们可以从更高的 MIP 级别进行采样,因为在平坦区域,这些包含简单的平均值。但是,我们必须保持内核的圆形形状。这是通过以最高分辨率收集最外层环,并为内层环增加 MIP 级别来实现的。
这允许减少总环数,从而减少样本数量。
组合通道
最终输出图像由全分辨率输入以及具有全分辨率 CoC 信息(通常来自深度缓冲区)的模糊近场和远场图像组成。
为了减少由于样本量少和随机子像素内核原点导致的模糊图像中的噪点和孔洞,应用了一些后处理。滤波后的图像使用双线性插值进行放大,并与全分辨率输入分层合成,全分辨率输入可以即时模糊以弥合全分辨率和半分辨率之间的差距。
后滤波
对近场和远场图像的颜色值应用 3x3 中值滤波器。该滤波器从邻域中选择中值亮度,并相应地调整中心像素的亮度。进行一些钳位以防止过亮。
在近场,对不透明度值应用 3x3 平均滤波器进行平滑。为了抵消边界值(0 和 1)周围的出血,使用三次函数将这些值映射到混合权重。
为了确保这些图层的连续性,中值亮度为零的像素不会改变,无效的近场像素会使用最高不透明度的角落像素进行填充。
向上采样
后滤波的近场和远场颜色使用双线性插值进行放大。由于两个图层中都可能存在无效像素,因此必须处理它们的插值。
在近场中,如果任何一个角的透明度为零,则插值将失效。在这种情况下,插值像素的透明度也设置为零。
在远场中,布尔有效性也进行插值,以便边界像素接收一个小数有效性值。然后用该值除以颜色,以有效地将有效区域扩展一个像素。这确保了图层之间的连续性,即使在它们不重叠的情况下。
固定 3x3 模糊
实时计算一个简单的固定大小的 3x3 像素模糊,以处理 CoC 值介于半个像素到两个像素之间的像素,这是半分辨率模糊无法实现的范围。
内核权重近似了以内核为中心的三像素直径圆的覆盖范围。
混合
所有图层颜色使用线性插值(标准 alpha 混合)混合在一起。基础颜色来自全分辨率输入。固定模糊和远场颜色在此基础上进行混合,根据该像素的 CoC 半径的绝对值。
对这些权重需要进行一些临时修复。如果远场颜色无效,则对应的权重设置为零。如果近场不透明度高于经验确定的阈值 0.3,则不应使用固定模糊颜色,否则可能会在边缘附近将前景对象的颜色泄露到背景中。
近场颜色使用基于滤波和插值的不透明度的三次权重在此颜色之上合成。
局限性
该技术无法处理半透明或体积效果,因为每个像素只能指定一个深度值。只要不修改深度,完全透明(不可见)的元素就可以正确处理。
在使用 SDK 中的预编译着色器时,仅支持圆形散景。对于其他形状,必须实现两个函数来指定每个环的样本数量和位置。
参考文献和进一步阅读
- “Depth of Field: A Survey of Techniques” in “GPU Gems”, Chapter 23, Joe Demers, 2004。
- “Graphics Gems from CryENGINE 3”, Tiago Sousa, in Advances in Real-Time Rendering in Games, SIGGRAPH 2013。
- “Next Generation Post Processing in Call Of Duty: Advanced Warfare”, Jorge Jimenez, in Advances in Real-Time Rendering in Games, SIGGRAPH 2014。
- “A Life of a Bokeh”, Guillaume Abadie, in Advances in Real-Time Rendering in Games, SIGGRAPH 2018。