FidelityFX 降噪器 1.3
AMD FidelityFX Denoiser 包含针对特定工作负载优化的专用时空降噪器
- 阴影降噪器 - 专为降噪由追踪有抖动的射线指向单一光源创建的阴影蒙版而设计。
- 反射降噪器 - 专为根据表面粗糙度去除追踪有抖动的反射射线结果中的噪点而设计。
AMD FidelityFX 阴影降噪器
引言
FidelityFX Shadow Denoiser 是一款用于光线追踪软阴影的时空降噪器。它旨在用于由每个像素最多一条有抖动的阴影射线生成的阴影蒙版。它利用瓦片分类通道来跳过在阴影蒙版中没有空间方差的区域的工作。在时间样本数量低的情况下,会增加空间滤波器的贡献,随着时间样本数量的增加,这种贡献会逐渐减弱。降噪器通过分析局部像素邻域并限制累积历史来避免拖影伪影。
着色语言要求
HLSL GLSL CS_6_2
输入
本节介绍 FidelityFX Shadow Denoiser 的输入。
| 输入名称 | 类型 | 说明 |
|---|---|---|
| hitMaskResults | Texture2D | 包含光线追踪通道结果的阴影蒙版,其中单个像素包含 8x4 瓦片的可见性结果 |
| depth | Texture2D | 该帧的深度缓冲区 |
| velocity | Texture2D | 该帧的运动矢量 |
| normal | Texture2D | 该帧的法线 |
| shadowMaskOutput | Texture2D | 阴影蒙版输出 |
| constants | 常量缓冲区 | 包含有关相机位置、各种变换矩阵和其他杂项设置信息的常量缓冲区 |
技术
以下是 FidelityFX Shadow Denoiser 的逐通道细分
Denoiser shadows prepare
光线追踪通道的输出被打包到一个缓冲区中,该缓冲区按照 32 位 uint 位掩码进行布局,以表示 8x4 像素的瓦片。这有助于降低后续通道的带宽。可以对光线追踪通道进行优化,使其作为一个组工作并直接写入这些位掩码。在这种情况下,可以跳过此准备通道。
虽然理论上可以将此想法扩展到每个像素一条射线以上,但当前实现的设计是针对每个像素一条射线。
Denoiser shadows tile classification
瓦片分类执行三个主要步骤
- 局部邻域编码
- 遮挡分离掩码生成
- 重投影
局部邻域编码
局部邻域编码存储了像素周围的着色值(在此情况下为有噪声的阴影值)的前两个矩(均值和方差)。
这些矩是
- 邻近样本的平均值。
- 方差。
矩是通过根据可变内核大小进行采样而创建的。以下是在重阴影运动下使用不同内核大小时的采样结果
| 5x5 内核 | 17x17 内核(默认) | 29x29 内核 |
|---|---|---|
![]() | ![]() | ![]() |
FidelityFX Shadow Denoiser 将内核半径值设置为 8,对应于 17x17 内核,并选择它作为质量和性能之间的平衡。
然而,17x17 内核需要每像素 289 次采样,成本过高。解决方案是认识到方差计算内核是可分离的,这意味着我们可以执行水平传递,存储中间结果,然后完成垂直传递。这会将每像素采样次数减少到两次传递共 34 次。利用压缩的射线命中位掩码,我们可以将 289 次采样进一步减少到 18 次标量加载。
为了避免将数据写入和读取到额外的中间目标所带来的内存带宽要求,水平传递和垂直传递都被集成到时间重投影传递本身中。这会带来需要重新计算一些水平值的成本(尽管由于使用了压缩阴影蒙版,这可以忽略不计)。
Disocclusion mask
遮挡分离掩码确定屏幕上的新区域。这些区域以前不在屏幕空间内,但由于摄像机运动或遮挡物移动而现在可见。
此信息通常被称为遮挡分离,在此通道中,我们确定一个二进制掩码,标记哪些像素被遮挡分离,哪些未被遮挡分离。
为了确定哪些区域是新的,我们计算每个像素在上一帧中本来应该具有的深度值,并将其与实际值进行比较,使用上一帧的深度缓冲区。如果深度值不匹配,则表示发生了遮挡分离。
为了计算上一帧的深度值,我们使用一个重投影矩阵,该矩阵将当前帧的裁剪空间位置转换为上一帧的裁剪空间位置,然后我们可以从中检索所需的深度值。
重投影值对数值误差敏感,因此我们建议使用更高的精度计算中间步骤
reprojection_matrix = view_projection_inverse_matrix * previous_view_projection_matrix;

在上图中,超过 1% 的深度误差足以触发遮挡分离,虽然这在一般情况下效果很好,但对于屏幕上深度变化较大的区域(通常发生在掠射角)则会失效。
通过根据表面掠射角(使用相机看向方向和表面法线的点积估计)动态更改每像素的阈值来解决此问题
| 恒定深度阈值 | 自适应深度阈值 |
|---|---|
![]() | ![]() |
请注意地板上由大深度变化引起的无效水平线,这些线条在自适应版本中被移除。
重投影
Reprojection 利用局部邻域编码数据、遮挡分离数据和提供的时间历史缓冲区来执行基于速度的重投影。
首先,我们计算时间方差,这是图像中噪声量的估计值。此值将由空间通道用来驱动降噪所需的模糊量

当由于被遮挡分离的区域重置每像素样本计数而导致可用的时间样本数量很少时,时间方差与局部邻域编码(在本通道的局部邻域编码部分中找到)中的空间方差结合使用
if (moments_current.z < 16.0f){ const float variance_boost = max(16.0f – moments_current.z, 1.0f); variance = max(variance, spatial_variance); variance *= variance_boost; // boost variance on first frames}在时间重投影无法正常工作的区域,我们会提高方差值,以消除噪点
| 无方差提升 | 有方差提升 |
|---|---|
![]() | ![]() |
请注意,在没有应用方差提升的情况下,图像的底部和左侧部分噪点明显更多。增加方差值意味着对时间历史很少或没有的像素进行更积极的空间模糊。这种模糊会随着历史的增长而逐渐消散。
一旦计算出矩和方差,阴影值将使用前一帧、历史缓冲区和降噪器样本的值的组合进行重投影。
这里的一个重要问题是阴影会移动,而速度图中并未反映这种移动。因此,需要一种方法来接受或拒绝这些情况下的历史样本。这是通过将重投影的历史样本限制在早期计算的局部邻域编码来实现的
| 朴素混合 | 邻域限制 |
|---|---|
![]() | ![]() |
请注意,在许多时间样本被错误混合的情况下,阴影会消失。相反,邻域限制有助于获得响应更快的过滤器,并在运动中保留阴影细节。
// Compute the clamping bounding boxconst float std_deviation = sqrt(local_variance);const float nmax = local_mean + 0.5f * std_deviation;const float nmin = local_mean - 0.5f * std_deviation;
// Clamp reprojected sample to local neighborhoodconst float shadow_previous = SampleHistory(uv - velocity);const float shadow_clamped = clamp(shadow_previous, nmin, nmax);邻域限制的实现类似于上述代码,可以在 FFX_DNSR_Shadows_TileClassification 函数的末尾找到。
然后,使用简单的指数移动平均混合将限制后的历史信息与当前帧合并。在合并时,请谨慎选择适当的混合因子,因为
- 低混合因子将保留当前帧的值,导致过滤器响应灵敏但不稳定。
- 高混合因子将保留限制后的历史值,导致过滤器稳定但不响应。
一种解决方案是根据可用历史的数量为每像素选择混合因子。在我们历史很少或没有的情况下,使用低混合因子,而在我们对重投影有信心的情况下,可以使用高混合因子
| 恒定混合因子 | 自适应混合因子 |
|---|---|
![]() | ![]() |
请注意,自适应版本如何产生响应更快的过滤器,消除了初始渲染中大部分的时间泄漏。
Denoiser shadows filter
FidelityFX Shadow Denoiser 的最后一个通道负责执行空间滤波,并运行三次。
这三个通道的采样区域相对较小,因此它们运行优化的内核,利用组共享内存来缓存样本。
它实现了 Edge-Avoiding À-Trous Wavelet (EAW) 滤波技术,在该技术中,模糊会以不断增大的半径值在多个通道中重复执行
无 EAW 通道 | 单次 EAW 通道 | 3 次 EAW 通道 |
|---|---|---|
![]() | ![]() | ![]() |
此外,在每个后续模糊通道后,都会用其滤波后的值更新时间通道中估计的方差。这会减少不需要的区域的模糊量
| 1 次模糊通道后的方差 | 2 次模糊通道后的方差 | 3 次模糊通道后的方差 |
|---|---|---|
![]() | ![]() | ![]() |
请注意,方差如何随着每次后续模糊通道而减少。
TAA
时间抗锯齿 (TAA) 通过对先前结果进行时间重投影来平滑边缘。TAA 可以在降噪器运行后进一步稳定和清理图像。
| 无 TAA | 有 TAA |
|---|---|
![]() | ![]() |
请注意,在没有 TAA 的渲染中,边缘会出现伪影,因为空间模糊无法找到匹配的样本。鼓励进行实验以确定适用于特定场景的 TAA 量。
AMD FidelityFX 反射降噪器
引言
FidelityFX Reflection Denoiser 是一款高性能的时空降噪器,专门用于反射降噪。使用瓦片分类通道来跳过非反射区域。该降噪器支持可变速率遍历:从镜像反射的全速率到粗糙反射的四分之一速率。它还支持时间方差引导的光线追踪。
着色语言要求
HLSL GLSL CS_6_0
输入
本节介绍 FidelityFX Reflection Denoiser 的输入。
| 输入名称 | 类型 | 说明 |
|---|---|---|
| depthHierarchy | Texture2D | 当前帧的带有完整 mip 贴图的深度缓冲区 |
| motionVectors | Texture2D | 当前帧的运动矢量 |
| normal | Texture2D | 当前帧的法线 |
| radianceA | Texture2D | 用于滤波的乒乓辐射度缓冲区 |
| radianceB | Texture2D | 用于滤波的乒乓辐射度缓冲区 |
| varianceA | Texture2D | 用于滤波和引导反射的乒乓方差缓冲区 |
| varianceB | Texture2D | 用于滤波和引导反射的乒乓方差缓冲区 |
| extractedRoughness | Texture2D | 当前帧的粗糙度 |
| output | Texture2D | 存储的降噪反射 |
| denoiserTileList | Buffer | 要降噪的瓦片 |
| indirectArgumentsBuffer | Buffer | 由降噪器调用的间接调度使用的间接参数 |
| constants | 常量缓冲区 | 包含有关相机位置、各种变换矩阵和其他杂项设置信息的常量缓冲区 |
技术
以下是 FidelityFX Reflection Denoiser 的逐通道细分
Reprojection pass
重投影通道检测遮挡分离、估算方差,并计算 8x8 辐射度 MIP。有两种重投影路径。
快速路径根据与局部邻域的相似性找到镜像反射视差,或使用表面运动矢量重投影,并根据邻域统计数据进行额外的距离检查以避免拖影。
慢速路径仅在边缘触发,尝试从 2x2 差值邻域构建更好的样本。8x8 平均辐射度也用于低样本区域和滤除异常结果。
Spatial denoiser pass
空间降噪通道利用方差引导的边缘感知滤波器来移除图像空间中的异常值和模糊。7x7 区域中的 15 个样本用于边缘感知模糊。每个样本的权重取决于其与 8x8 平均值的相似度。
Temporal denoiser pass
时间降噪通道移除异常值,并使用 9x9 高斯区域统计数据来限制历史(来自重投影通道的重投影结果)。新信号根据样本数量与历史混合。


















