光追预算

实时光线追踪 Real-Time Ray Tracing,RTRT 在帧预算内查询场景几何,可用于软阴影、反射、AO 和间接照明。与 SSR 相比,场景射线能访问屏幕外或当前相机遮挡后的表面;硬件遍历与求交单元可以加速这些查询,但不会自动解决采样方差或材质求值。

镜面方向集中,少量射线就可能表现主要反射路径;漫反射要覆盖宽方向范围,有限样本的噪声更明显。这也是早期光追演示常用光泽、镜面材质表现反射的原因,不能由此认为光追只适合反射。

课程以 2018 年 RTX/Turing 为硬件背景,RT Core 加速 Bounding Volume Hierarchy,BVH 遍历及射线与三角形求交。BVH 用逐级包围盒组织几何,先排除射线不可能碰到的范围,减少直接测试三角形的数量;专用求交硬件不负责自动完成所有 BRDF、光传输与图像重建。

每像素样本数 Samples Per Pixel,SPP 统计用于估计像素的样本。一个路径样本可能产生多条射线:相机射线、反弹射线与通向光源的可见性射线都可能计入查询,因此 SPP 不等于硬件总射线数。

课程用当时约 1010 Giga rays/s 的量级说明吞吐与帧预算问题,这类峰值依赖硬件与场景,不能直接换算成固定 11 SPP。若有效吞吐为 RR,分辨率 W×HW\times H、帧率 FF,每个路径平均需要 nrn_r 条查询,则理想样本预算约为 R/(WHFnr)R/(WHFn_r);实际还要留出光栅化、着色、内存、降噪和其他效果的时间。课程所讨论的约 11 SPP 是低采样设计情境,不是所有实时光追的定义。

短路径

可以先用光栅化获取相机可见的第一表面,省去逐像素追踪主相机射线。之后在这个点计算直接光,并追踪一次间接反弹。

光栅化主交点后的一次间接反弹与两次光源可见性查询

下方是 Primary Hitpoint,左墙是 Secondary Hitpoint。两条指向光源的虚线用于测试主、次交点到光源的可见性,连接两个表面的实线对应反弹路径。图中箭头同时示意光的传输关系;从相机端追踪时,先在主点采方向,再向次点查询。

这个例子的每像素成本包含一次光栅化、主点阴影射线、反弹射线和次点阴影射线,即三次射线查询。主点和次点各做一次材质求值,主点还要采一次反弹方向。它形成直接项与一次间接项的估计,不能因此说一条路径只能含一次查询。

蒙特卡洛估计 Monte Carlo Estimation 对独立样本求平均。有限方差时,样本均值方差按 1/N1/N 减少,标准误差按 1/N1/\sqrt N 减少。低 SPP 下,同一像素这一帧可能碰到亮光源附近,下一帧只取得暗方向,产生较大变化;许多噪声不是高斯白噪声,少数很亮的路径还可能形成长尾分布。

重建目标

降噪 Denoising / Reconstruction 用有限样本和辅助信息估计更稳定的图像。目标同时包括降低噪声、保留细节、减少闪烁与拖影,并控制运行时间。扩大空间滤波能压低噪声,也可能抹去接触阴影、细纹理和高光。

已有方向包括按光传输频率分析设计的轴对齐或剪切滤波、离线高样本降噪、卷积网络与循环网络。轴对齐或剪切滤波分析的是特定光路信号的相关性,需要对应问题条件;网络也需要训练数据、输入属性与推理预算。课程当年展示的 OptiX 单帧降噪器作为只利用当前帧的对照,与后面的序列重建输入不同。课堂比较的是当年的具体方案,不能概括为所有神经网络都无法实时。

降噪与超分辨率解决不同问题:前者降低采样估计的噪声,后者恢复更高空间分辨率,两者可在同一管线中结合。无偏的路径样本经过自适应滤波、限幅或历史选择之后,通常会引入偏差,图像更平滑不等于估计仍然无偏。

历史复用

单帧只用当前附近像素,会将不同表面的信息混合。相邻帧往往看到了相同的表面点,如果能找到对应位置,就可以继续利用上一帧已经降噪的结果。

当前帧圆形物体通过运动向量找到上一帧位置

左图是上一帧,右图实线圆是当前位置,虚线圆是此前位置。箭头从当前指向历史,表示应该去哪里读历史颜色。不能直接读取同一个屏幕坐标,否则移动物体会混入背景或留下一条旧轮廓。

运动向量 Motion Vector,MV 记录当前表面点的屏幕对应位置。这里约定 m=q−p\mathbf m=\mathbf q-\mathbf p,p\mathbf p 是当前像素 UV,q\mathbf q 是同一表面点的上一帧 UV,查询位置为 q=p+m\mathbf q=\mathbf p+\mathbf m。有些实现存相反方向或像素单位,读取时必须配套转换。

光流 Optical Flow 通常从两幅图像估计表观运动;渲染器则知道相机和物体的变换,可以直接计算几何对应。不过,知道几何对应不等于知道历史位置是否保存了同一表面,更不等于该点照明保持不变。

几何缓冲

G-buffer 保存相机可见表面的深度、法线、材质、位置或标识等属性。它们可以与主可见性光栅化一起输出,为重投影和保边滤波提供数据。额外附件仍占带宽和内存,也增加生成成本,不能把“可以顺便输出”理解为零开销。

完整世界位置无需一定存三份浮点数:已知像素和深度,可以通过逆投影重建。法线和材质属性可帮助判断历史是否可信。所有缓冲只描述当前可见层,缺失的背景、背面与屏幕外区域仍不能直接查询。

逆向投影

重投影 Reprojection / Back Projection 先找到当前像素看到的表面点,再问它在上一帧投影到哪里。

同一盒面点在两帧中的对应位置

右侧 Frame ii 的蓝点投回左侧 Frame i−1i-1;物体或相机改变后,两点屏幕坐标不同,但对应同一个表面位置。箭头不是将整个图像统一平移,不同物体和深度会有不同向量。

以 OpenGL 默认 NDC 深度 [−1,1][-1,1]、深度缓冲 d∈[0,1]d\in[0,1] 为例,像素 UV (u,v)(u,v) 先变为

xndc=(2u−1,2v−1,2d−1,1)T.\mathbf x_{\rm ndc}=(2u-1,2v-1,2d-1,1)^{\mathsf T}.

这里 UV 原点与视口的竖直方向一致;若纹理使用左上原点,还要翻转 vv。其他深度范围或 Reverse-Z 应使用其投影矩阵和对应转换。

当前投影、视图矩阵为 Pt,VtP_t,V_t。逆投影后要做齐次除法:

x~v=Pt−1xndc,xv=(x~v,xyzx~v,w,1)T,xw=Vt−1xv.\widetilde{\mathbf x}_v=P_t^{-1}\mathbf x_{\rm ndc}, \quad \mathbf x_v=\left(\frac{\widetilde{\mathbf x}_{v,xyz}} {\widetilde x_{v,w}},1\right)^{\mathsf T}, \quad \mathbf x_w=V_t^{-1}\mathbf x_v.

对模型矩阵为 MtM_t 的刚性物体,局部表面坐标是 xo=Mt−1xw\mathbf x_o=M_t^{-1}\mathbf x_w。用上一帧物体姿态与相机变换,得到

cprev=Pt−1Vt−1Mt−1xo.\mathbf c_{\rm prev}=P_{t-1}V_{t-1}M_{t-1}\mathbf x_o.

再除以 cprev,wc_{\mathrm{prev},w},将 NDC x,yx,y 变为 q=(xprev,ndc,xy+1)/2\mathbf q=(\mathbf x_{\mathrm{prev},\mathrm{ndc},xy}+1)/2。这一步包括上一帧视口/纹理坐标变换,不能停在裁剪坐标就将其当成像素地址。齐次 ww、裁剪范围和屏幕边界也要检查。

如果已经有世界位置,可以跳过当前逆投影;骨骼、变形或顶点动画则需要上一帧对应的变形位置,只有两个刚性模型矩阵不足以恢复。投影抖动、动态分辨率、曝光和颜色域变化,也应在对应与历史读取时一致处理。

时间加权

先将当前带噪结果做空间过滤,得到 C‾t(p)\overline C_t(\mathbf p);再从历史位置 q\mathbf q 读取上一帧重建颜色 Ht−1(q)H_{t-1}(\mathbf q),进行时域累积 Temporal Accumulation:

Ct(p)=αC‾t(p)+(1−α)Ht−1(q).C_t(\mathbf p)=\alpha\overline C_t(\mathbf p) +(1-\alpha)H_{t-1}(\mathbf q).

课程举 α=0.1\alpha=0.1–0.20.2 的例子,当前结果占 10%10\%–20%20\%,历史占 80%80\%–90%90\%。这里的历史是已经重建的上一帧,不是仅含一个旧样本,因此递归展开后,较早帧的信息也会保留:

Ct≈αXt+α(1−α)Xt−1+α(1−α)2Xt−2+⋯ .C_t\approx\alpha X_t+\alpha(1-\alpha)X_{t-1} +\alpha(1-\alpha)^2X_{t-2}+\cdots.

这称为指数移动平均 Exponential Moving Average,EMA。各帧权重沿时间衰减,和为一。不能把公式中的历史理解为同屏坐标,也不能将 1/α1/\alpha 直接当成“只记住这么多帧”的硬截止。

为理解降噪效果,假设对应点信号稳定、样本独立同分布、各样本方差为 σ2\sigma^2,且忽略空间过滤与重采样相关性,则

Var⁡(C)=σ2∑k=0∞α2(1−α)2k=α2−ασ2.\operatorname{Var}(C)=\sigma^2\sum_{k=0}^\infty \alpha^2(1-\alpha)^{2k} =\frac\alpha{2-\alpha}\sigma^2.

相对于独立等权平均,有效样本数为 Neff=(2−α)/αN_{\rm eff}=(2-\alpha)/\alpha,α=0.1\alpha=0.1 时约为 1919。真实帧间样本可能相关,历史还会被拒绝或限幅,不能保证始终获得这个样本数。光照变化时,小 α\alpha 会更久保留旧值,降噪与响应速度之间因此存在取舍。

初次进入场景没有历史,应从当前有效估计初始化;每帧结果写入新的 History Buffer,再与旧缓冲交换,避免当前像素读取到本帧已经更新的其他像素。

重建对照

室内场景的1SPP带噪输入

这是低采样输入,同一面墙、桌面和地板上的亮暗差别包含较多随机变化。多数位置偏暗,少数路径得到很大贡献;仅靠当前像素难以判断真实照明。

同一区域经过时空重建后的结果

重建后能分辨墙面、楼梯与桌椅,但椅脚接触、桌下阴影和一些地板细节也可能被平滑。

同一区域的参考全局光照结果

与参考图比较,不能只观察是否还有噪点,还应看阴影形状、材质细节与运动时的稳定性。这三幅是相同裁区的课程对照,不是分别从不同场景选择最好看的部分。

噪声预览显暗也可能由显示处理造成。例如样本以 0.90.9 概率为零、0.10.1 概率为十,线性期望为一;若每个样本先截到 [0,1][0,1],显示平均只有 0.10.1。因此先截高亮或执行非线性 Tone Mapping,再平均,与先在线性 HDR 域重建再显示并不等价。不能由“去噪图更亮”推出任意滤波都提高能量;归一化加权平均能保常量,也不保证任意保边、限幅或边界处理都保持全图总能量。

历史失效

Temporal Failure 表示历史不能可靠参与当前估计,常见来源包括:

  • 切换场景或镜头后,旧图像没有对应意义,应重置历史。灯光突变也可能使同一几何点的旧颜色失效。
  • 相机退后或转动,当前像素对应的旧位置落在屏幕外,没有可读历史。
  • 前景移动后背景显露,上一帧对应位置保存的是前景,而不是当前背景。

第三种称为遮挡解除 Disocclusion。

背景点新显露时其上一帧投影位置保存了前景盒子

右图蓝点在背景管道上,几何重投影把它映到左图盒子覆盖的位置。对应坐标可以完全正确,但历史缓冲只保存了最近的盒子,没有背景的样本。继续混合会将盒子颜色带到背景,形成鬼影 Ghosting 或滞后。

重置之后需要重新积累,这一阶段常称 Burn-In。渐入显示可能缓和视觉变化,但不能使无效历史重新有效;背景露出时,必须接受暂时更多噪声或更强空间重建。

有效性检查

重投影后先检查屏幕范围与历史是否初始化,再比较历史位置的几何属性。上一帧预测深度应由当前表面点变换到上一帧得到,再与历史深度比较;相机移动时直接比较两帧同位置的原始深度是不正确的。法线也应转到共同坐标空间后比较。

Object ID、材质 ID 或其他标识可发现部分不匹配,但同一个物体上不同面、重新分配的 ID 与同几何上的光照变化仍可能通过。双线性读取历史时,附近四个像素可能属于不同表面,插值前后也要考虑有效性,避免混入前景边界。

若历史无效,可以丢弃或降低其权重,将 α\alpha 提高;必要时增强当前帧空间滤波。这样减少拖影,同时会重新出现噪声,或增加空间模糊。

Temporal Clamping 将历史颜色限制到当前邻域支持的范围。例如当前 7×77\times7 邻域的均值、标准差为 μ,σ\mu,\sigma,使用

H′=clamp⁡(H,μ−kσ,μ+kσ).H'=\operatorname{clamp}(H,\mu-k\sigma,\mu+k\sigma).

限制方向是“历史向当前靠近”,不能反过来强迫当前结果接近旧照明。当前邻域也可能有噪声、材质边或真实高光,范围越紧,旧色越快被去除,也越少保留历史降噪贡献;颜色空间与各通道统计会影响具体效果。

着色运动

几何运动向量追踪的是表面点,照明特征不一定跟着表面运动。

静止栏杆与地面在移动光源下形成滞后的阴影

相机、栏杆和地面都不动,只移动光源,几何 MV 可以为零,但阴影在地面移动。左图为参考,右图阴影边界保留了过去位置;彩色条带标出当前与历史阴影的差别。深度、法线与 Object ID 仍可能一致,单纯几何有效性检查发现不了这种着色失效。

静止地面上的椅子反射随椅子位置变化

这是用于说明反射问题的场景:地面点保持静止,椅子移动会改变该点的反射来源。如果继续复用同一地面点的旧色,反射便可能拖后。单张画面本身不展示完整运动过程,但能看到接收面与被反射物体是不同几何对象。

观察角变化也会改变 Glossy 响应,几何点相同不代表出射辐亮度相同。着色对应 Shading Correspondence 因此需要考虑形成阴影、反射或遮挡的原因。

Temporally Reliable Motion Vectors 进一步跟踪这些成因,以利用更多时域信息;它扩展了基础几何对应,仍不能制造屏幕外的历史,也不能让场景切换的旧数据有效。原论文

累积流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 当前估计与历史都在相同的线性信号域,alpha 乘当前结果。
for each current pixel p:
current = spatialFilter(noisyCurrent, currentGBuffer, p)
q, predictedPreviousDepth = reprojectCurrentSurface(p)
valid = insideHistoryScreen(q) and historyInitialized(q)
valid = valid and geometryMatches(q, predictedPreviousDepth)

if not valid:
newHistory[p] = current
continue

history = readCompatibleHistorySamples(q)
mean, deviation = currentNeighborhoodStatistics(p)
history = clamp(history, mean - k * deviation, mean + k * deviation)
alpha = chooseCurrentWeight(historyConfidence, shadingChange)
newHistory[p] = alpha * current + (1 - alpha) * history
swap(oldHistory, newHistory)

历史色、深度、法线与标识应来自同一历史状态。改变分辨率、曝光或投影抖动时,应正确转换或重置;只有颜色缓冲更新了、几何验证仍留在更早一帧,也会破坏对应。

Temporal Anti-Aliasing,TAA 同样利用跨帧对应积累样本,只是侧重空间采样不足造成的走样。它与低样本降噪共享历史有效性、拖影和限幅问题,但不能把消除走样与估计随机光传输噪声视为完全相同的目标。