光追降噪
全库目录221
本文目录13
原始光追噪声只有颜色,滤波不知道相邻样本是否来自同一表面。降噪加入深度、法线、粗糙度与命中距离,再决定历史与邻域能否复用。拒绝太松产生拖影,太严则保留噪声,诊断要看具体拒绝原因。
时空滤波
几何辅助缓冲
原始辐亮度只有颜色,很难判断两个相邻样本是否属于同一表面。降噪器常使用:
- Linear 深度;
- World/视图法线;
- 粗糙度;
- 材质/实例ID;
- 运动向量;
- 命中距离;
- 样本数、方差、置信度。
辅助缓冲必须与射线结果来自同一几何和相机时刻。错误运动向量或不同法线空间会造成比噪声更明显的拖影。
时域积累
上一帧结果通过运动向量重投影到当前像素:
若历史有效,则:
稳定区域使用小 积累更多样本;运动、光照变化或高方差区域提高 ,快速响应当前帧。
若历史已有3个等权样本、均值2,当前样本为6,历史权重3/4,结果为3。这个递推保留四个样本的算术平均;设最大历史权重后则成为有限响应的指数平滑,sampleCount不再直接表示独立有效样本数。遮挡揭露时权重归零,输出当前6,避免旧表面的2进入新表面。
遮挡揭露与历史拒绝
重投影位置存在历史像素,不代表历史属于当前表面。需要比较:
- 深度/世界位置;
- 法线;
- 材质/实例ID;
- 粗糙度;
- 命中距离;
- 对象/光照状态变化。
镜面反射的运动不能只用主表面运动向量。反射内容来自次级命中,镜头和反射面运动会产生复杂 Parallax。实时方案常依赖更严格的 Reject、较短历史和空间补偿。
时域积累的读取与拒绝
当前像素通过运动向量回投到上一帧。深度、法线和对象 ID 共同决定历史是否仍属于同一表面:
float2 historyUv = uv + motionVector;
History h = ZeroHistory();
bool valid = all(historyUv >= 0.0) && all(historyUv <= 1.0);
if (valid) h = LoadHistory(historyUv);
valid = valid
&& abs(h.linearDepth - currentDepthInPreviousView) < depthThreshold
&& dot(h.normalWS, normalWS) > normalThreshold
&& h.objectId == objectId
&& all(isfinite(h.radiance)) && all(isfinite(h.normalWS))
&& isfinite(h.linearDepth) && isfinite(h.sampleCount)
&& h.sampleCount >= 0.0;
float3 accumulated = currentRadiance;
if (valid) {
float historyWeight = min(h.sampleCount / (h.sampleCount + 1.0), maxHistory);
accumulated = lerp(currentRadiance, ClampHistory(h.radiance), historyWeight);
}调用者提供有限的当前颜色、可比深度、法线、非负深度阈值以及[0,1)内的maxHistory。先检查回投UV,再读取历史资源;拒绝历史时直接保留当前颜色,避免零权重乘NaN仍污染结果。有限但负的历史计数也按损坏记录拒绝。深度比较也要使用可比的相机空间:相机移动时,当前观察深度不能直接与前帧观察深度相减,可以把当前世界位置变到前帧视空间后比较。运动向量方向与回投公式保持一致。拒绝过严会重新出现噪声,过松会产生拖影。用静止相机、快速横移、遮挡显露和发光物突变四组场景分别检查收敛、遮挡揭露与历史限制范围。
历史限制范围
错误历史会产生拖影。常在当前邻域计算最小值和最大值或 Mean/方差,把历史颜色限制到可信范围。
方差 Clipping 可写为:
和 来自当前邻域。 小会过度抹平, 大则残留历史 Ghosting。
HDR Firefly 会污染 Mean/方差。可先在 Luminance 或压缩颜色空间处理,并使用 Robust Statistics。
空间滤波
Cross-bilateral Filter 同时考虑屏幕距离、深度、法线、颜色和材质:
例如中心亮度1,左邻居同表面亮度3且权重1,右邻居来自背景亮度100、深度权重0,归一化结果为(1+3)/2=2。若忽略深度门限就会得到104/3,轮廓出现漏光;若权重和没有归一化,改变有效邻居数量本身也会改变亮度。
A-trous Wavelet 每轮扩大采样 Step,不必降采样就能快速扩大感受野。迭代太多会丢高频阴影和反射细节。
Specular Filter Radius 应与粗糙度、命中距离和射线锥相关。镜面反射不能像 Diffuse GI 一样大范围模糊。
方差与自适应采样
可用一阶矩 和二阶矩 估计:
高方差区域可以增加空间过滤、降低历史信任,或在预算允许时分配更多射线。方差自身也有噪声,需要时间积累与数值保护。
降噪器常见伪影
- Ghosting:历史没有及时 Reject;
- Boiling:随机噪声在时间上持续变化;
- Blurring:空间 Filter 跨越细节或半径过大;
- 光源 Leak:深度/法线 Gate 太宽;
- Dark Halo:遮挡揭露没有足够新样本;
- Firefly Trail:极亮样本进入历史;
- 反射 Lag:反射运动与主表面 Motion 不一致。
处理时先输出历史 Valid、样本数、方差、命中距离与 Filter Weight,不要只看最终颜色猜原因。
与 TAA/Upscaler 的关系
射线降噪器和 TAA/Upscaler 都使用时域信息,但负责的信号不同。降噪器应尽量输出稳定的光照信号;Upscaler 再处理最终画面的分辨率重建和抗锯齿。
两者连续积累会放大拖影。需要明确执行顺序、Jitter、运动向量、Exposure 和 Reactive 遮罩,不应把“后面还有 TAA”当作降噪器可以省略的理由。
历史裁剪与空间滤波分别改变信号。邻域均值和方差最好在约定的亮度或颜色空间计算,并使用与历史相同的曝光尺度;曝光翻倍而历史没重标定时,即使表面完全静止,也会被当成异常颜色。下方 ClampHistory 表示前述 的分量裁剪,当前帧邻域、参数 k 和色彩空间由调用者提供,不是任意黑盒修复。
验证方法
保留原始、只时域、只空间与最终结果,记录历史长度、方差和Reject Reason。深度比较先转到前帧可比空间,越界UV先拒绝再读历史。镜面次级运动与主表面速度分开核对。
正文抽取检查将相同控制流与算术编译为C++17 CPU宿主,检查正常四样本均值3、无效NaN历史、负计数和越界不读取;向量与资源读取由宿主替代,完整HLSL管线与GPU画面未运行。
相关主题
参考资料
- Schied 等:Spatiotemporal Variance-Guided Filtering:作者摘要:时域积累、亮度方差和多尺度滤波;本文历史拒绝阈值是示例设计。