想象你是一位主厨,站在一间断电的黑暗厨房里,想判断面前一锅汤是咸是淡。你手边只有一根筷子,每次只能蘸起一小滴来尝。光线追踪渲染器面临的正是同样的窘境:每一个像素就像一锅汤,渲染器向场景发射的光线就是那根筷子。光线数量少,尝过的点就稀疏,整幅画面便会蒙上一层颗粒般的噪点。要让画面变干净,大厨的办法不是疯狂蘸汤,而是三招:记住过去(时域)、借鉴邻居(空域)、依靠经验(AI)。这就是实时降噪 —— OptiX 和 OIDN 的底层智慧。这篇文章会用厨房里的故事,带你一步步看懂降噪技术如何让光追画面从“满是胡椒粒”变得“细腻丝滑”。
掌握实时降噪原理,是图形与 AI 交叉方向的高保值技能;能听懂 OptiX 与 OIDN 的取舍,你才能在设计渲染器、调画质时拿回主动权,真正对帧率和清晰度负责。
可视化演示
动态演示实时降噪的核心机制,通过概览/流程/对比/趋势四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
实时降噪:实时降噪 实时降噪问题背景与挑战 时域降噪与历史帧累积 核心机制可视化 实时降噪整体架构与关键组件
噪声源头
想象一间断电的黑暗厨房。你是一位主厨,只能靠一根筷子蘸汤来判断味道。光线追踪渲染器也是如此:每个像素像一锅汤,渲染器向场景发射若干条光线,光线就是筷子。光线数量太少,尝过的味道点稀疏,画面就布满颗粒——这就是噪点。想要判断整锅汤的咸淡,你必须在有限次数里尽量“尝到关键位置”,这正是蒙特卡洛方法在做的事。
这种“用随机样本估计像素颜色”的方法叫蒙特卡洛路径追踪。每像素采样数(samples per pixel,SPP)就是筷子蘸汤的次数。SPP = 1 时,画面上嘈杂的颗粒感几乎无法忍受;SPP 提升到 1024,画面才称得上干净,但渲染时间可能暴涨数百倍。实时渲染通常只给得起每像素 1~4 个样本,噪点问题因此格外严重。
路径追踪的数学本质是对渲染方程做数值积分。假设我们采样 \(N\) 条光线路径,每个路径 \(\mathbf{x}_i\) 按概率密度 \(p(\mathbf{x}_i)\) 生成,那么像素颜色的估计值可以写成:
1. 随机采样:从概率分布中生成 \(N\) 个光线路径样本,对应主厨用筷子蘸 \(N\) 次汤。
2. 辐射评估:对每个路径计算光照贡献 \(f(\mathbf{x}_i)\),即这滴汤带来的“味道信息”。
3. 概率修正:将贡献除以概率密度 \(p(\mathbf{x}_i)\),因为有些路径更常出现,必须按其稀少程度放大权重。
4. 算术平均:把所有修正后的样本相加并除以 \(N\),得到一个像素的颜色估计。
蒙特卡洛估计的误差随样本数平方根缓慢下降:样本数变为 4 倍,标准误差才降低一半。换句话说,要少一半噪点就要多花四倍时间,这让“光线追踪不能直接实时渲染”成为业界共识。既然不可能无限增加蘸汤次数,大厨只能转而依靠另外三样东西:记忆、邻居和老师傅的经验。
时域降噪
试想一个场景:你刚才尝过这锅汤,接着助手将它端向右边。你完全不需要从头再尝一遍,只需要记住刚才那一口的位置,估算出锅子移动的方向与距离,就能把旧笔记换算到新位置继续使用。这就是时域降噪的基本哲学——上一个画面的信息非常宝贵,不该浪费。
时域降噪的核心是历史帧复用:把上一帧已经累积过的干净结果,通过运动矢量(Motion Vector)重投影到当前像素位置,再与当前帧的新样本做加权混合。运动矢量记录了每个像素从上一帧到这一帧的移动量,它让时间维度上的“对齐”成为可能。渲染器也输出深度、法线等信息,用来辅助判断哪些历史信息仍然可信。
一帧画面的时域累积可以用一个简洁的递推式表达:
1. 运动矢量求解:\(\mathbf{v}_t\) 表示当前像素在帧间的位移,由相机变换与物体运动共同导出,相当于记下“锅往哪边挪了几厘米”。
2. 历史帧重投影:找到当前帧位置在上一帧对应的像素,取出该处已经降噪完毕的颜色。
3. 累积系数:\(\alpha\) 控制当前帧新样本的信任程度,\(\alpha\) 越大代表越依赖新信息,历史帧保留得越少。
4. 指数加权混合:把当前帧新样本与历史帧结果按系数 \(\alpha\) 融合,形成这一帧的输出。
实际操作远不止套公式。当物体被遮挡,例如碗碟突然挡住了移动中的锅,对应的历史像素已经失效。渲染器会在重投影后比较深度差,如果深度不一致就调大 \(\alpha\),甚至完全丢弃历史帧,避免上一帧的内容拖出长长的“鬼影”。这种遮挡检测是时域降噪的关键一环,它决定了画面在剧烈运动时能否保持干净而不出现残影。
用厨师的话说:记忆帮我们省下大量重尝的功夫,但必须在“锅被换掉”时果断放下旧笔记,只信当前这一口。时域降噪把帧率提升了一个量级,但单靠它还不足以消除所有闪烁,接下来需要空间维度帮忙。
空域滤波
深度记忆只能解决时间维度的问题,画面里仍然可能残留零星噪点。这时大厨会看向邻桌:自己的汤淡了,就向旁边借一点酱汁。但借之前必须判断对方碗里是不是同一种食材——如果把巧克力酱倒进番茄汤,整道菜就毁了。空域滤波正是“向邻居借信息”,但需要有分辨能力。
最朴素的空域滤波是高斯滤波,它对邻域内所有像素做加权平均,距离越近权重越大。但它有一个致命缺点:无差别地把边缘两侧的颜色混在一起,导致画面发糊。双边滤波(Bilateral Filter)解决了这个问题,它在高斯核的基础上增加了一个“值域核”,如果两个像素的颜色差异太大,就认为它们属于不同边缘区域,降低彼此权重。
双边滤波中,像素 \(j\) 对像素 \(i\) 的滤波权重为空间核与值域核的乘积:
1. 空间距离:\(\|\mathbf{p}_i-\mathbf{p}_j\|^2\) 计算两个像素在屏幕坐标上的欧氏距离,距离越远,空间权重越低。
2. 空间核宽度:\(\sigma_s\) 控制邻域范围,相当于“手臂伸多长去借调料”。
3. 像素值差异:\(|I_i-I_j|^2\) 比较两者颜色的差异,差异越大,说明很可能跨过了一条几何边缘。
4. 值域核宽度:\(\sigma_r\) 定义“味道差多少算不同菜”,它越小对边缘越敏感,边缘保留越强。
5. 权重相乘:两个指数函数相乘,只有“既近又相似”的像素才有资格贡献信息。
工程上还会使用交叉双边滤波(Cross Bilateral),用反照率、法线等几何特征作为引导图,把材质边缘与照明边缘分开处理。例如一面白墙和一件白衬衫颜色接近,但法线不同,引导图能防止墙上的噪声污染衬衫的纹理。这就是“边缘保持”背后的真实意图——不是不模糊,而是只在正确的区域模糊。
厨师的借料原则总结起来是八个字:空间相近,味道相近。空域滤波把残噪抹平而不伤细节,但纯手工设计的权重始终有上限。此时,见过无数锅汤的老师傅登场了。
OptiX降噪
NVIDIA 的厨房里住着一位见多识广的 AI 主厨。他不需要一口一口尝汤,而是瞄一眼粗糙的汤色、食材分布和锅的深浅,就能凭经验推断出整锅汤应有的味道。这位主厨就是 OptiX 降噪器背后的深度学习模型,它学习了海量“噪声图 → 干净图”的对应关系,部署在 GPU 的 CUDA 核心上,以极快的速度完成前向推理。
OptiX 降噪不是简单地把一张噪声图丢进网络。它要求渲染器额外提供几组特征图,帮助网络理解场景结构:噪声颜色是“当前能看到的画面”,反照率告诉网络物体本来的颜色,法线反映表面朝向,深度提供远近关系。这些特征让网络能够区分几何边缘与光照变化,避免把颜色从一个物体渗到另一个物体上。
整个推理流程可以浓缩成一个函数:
1. 输入特征组:把噪声图与反照率、法线、深度特征打包,相当于给主厨准备好汤色、食材清单和锅的深度信息。
2. 网络前向推理:\(f_\theta(\cdot)\) 是训练好的卷积神经网络,在 CUDA 核心上执行一次前向传播,直接还原干净图像。
3. 学习参数:\(\theta\) 是神经网络权重,来自离线训练阶段对数百万帧样本的学习,相当于主厨多年练就的直觉。
4. 输出:\(\hat{I}\) 是去噪后的像素颜色,可直接送往后处理管线,完成最终的合成帧。
为什么特征输入如此重要?如果没有反照率,网络看到的是一个暗红色球体和一个亮红色球体,它无法判断哪一个的“本色”是红色,于是可能把暗部的噪声误当成材质细节。反照率图直接告诉它“这里底色是什么”,从而大幅降低颜色渗入。法线和深度则帮助网络尊重几何边界,让边缘锐利如刀。
这位 AI 主厨虽然经验老到,却也有自己的“脾气”:它高度依赖特征的准确性,也依赖 NVIDIA 硬件。若你想在 CPU 或非 RTX 设备上运行,就需要另一位开源顾问——OIDN。
OIDN降噪
Intel 的厨房里有一位乐于分享食谱的 AI 顾问,名叫 OIDN(Open Image Denoise)。它不像 OptiX 那样必须用 NVIDIA 专用厨具,而是把完整的“食谱”——模型结构和训练权重——都开源给你,让你在 CPU、GPU 各种平台上自由开火。对很多无法使用 OptiX 的渲染器来说,OIDN 是性价比极高的选择。
OIDN 采用的网络结构是 U 型编码-解码器(U-Net):编码器不断下采样压缩特征,解码器逐渐恢复全分辨率,中间的跳跃连接把浅层细节传递给深层,避免丢失纹理。它在训练阶段见过海量含噪渲染图,学会了从“粗糙汤色”还原“真实味道”的映射关系,推理时数毫秒即可完成一张降噪图。
训练 OIDN 模型的目标函数可以写作:
1. L1 重建损失:计算去噪结果与参考图逐像素的绝对差,数值越小代表像素级越接近。
2. 感知损失:比较高层次特征空间中的差异,帮助模型保留人眼敏感的结构与纹理观感。
3. 平衡系数:\(\lambda\) 调节像素准确与观感自然之间的比重,太大会让画面“过于艺术”,太小则偏像素死板。
4. 期望操作:\(\mathbb{E}[\cdot]\) 表示在大规模训练集上取平均,让模型学会各种场景下的通用规律。
实际对比中,OIDN 模型更轻量,对 CPU 离线渲染和低功耗 GPU 十分友好;OptiX 则在 NVIDIA RTX 显卡上拥有更快的推理速度与更成熟的工程集成。两者并非绝对优劣,而是平台决定论:厨房里有什么厨具,就选哪位师傅。理解了这两种方案,你就能在真实项目中做出合理取舍。
调优落地
再好的汤,如果让客人等上一小时,也失去了意义。实时降噪的工程落地,本质是一场性能与画质之间的交易:把降噪的时间预算、渲染管线调度、画质验收指标都摆上台面,像大厨安排晚宴节奏一样精细调配每一道工序。
实践中,降噪绝不是“调一个 API”就能完事。你需要回答几个问题:降噪计算分到多少毫秒?时域累积系数在运动剧烈时怎么变化?降噪后的帧在连续播放时会不会闪烁?这些都会影响最终用户体验。质量验收通常结合数学指标与人工观察:PSNR 反映像素级误差,而 SSIM 更贴近人眼对结构相似度的判断。
SSIM 在局部窗口内比较两幅图像,其核心公式为:
1. 亮度相似:\(\mu_x, \mu_y\) 是两幅图局部窗口的均值,比较窗口内整体亮度是否一致。
2. 对比度相似:\(\sigma_x^2, \sigma_y^2\) 是方差,反映局部纹理的浓淡程度,方差接近说明清晰度相似。
3. 结构相似:\(\sigma_{xy}\) 是协方差,衡量两个窗口内亮度变化的同步性,数值接近意味着结构相似。
4. 稳定常数:\(c_1, c_2\) 防止分母接近零时结果爆炸,同时控制指标对亮暗变化的敏感度。
在工程集成上,常见手段包括:把降噪放入异步计算队列,与主渲染管线并行;用上一帧的 GBuffer 提前启动降噪;在动态分辨率渲染中降低分辨率后再用空域滤波恢复。调优时还要注意时序稳定性——静态帧再漂亮,播放时闪烁一下就会前功尽弃。
最终,一顿完美的晚宴背后是每道菜的时间、火候与卖相的全局协调。实时降噪也一样:噪声源头是有限的采样,时域用自己的积累补足样本,空域用邻域信息填平残噪,AI 再用经验把所有线索整合成干净画面。理解这条链路,你就能在帧率与画质的跷跷板上稳稳站住。
AI 应用场景
游戏引擎中的实时光追
阶段:模型推理 + 实时管线集成
解决的问题:传统光追每像素需要 256 甚至 1024 个样本才能获得干净画面,游戏却必须在 16.6ms 内完成整帧渲染。AI 降噪将每像素采样降到 1~4 个,再用时域累积 + OptiX/OIDN 补足质量,让实时光追真正跑上游戏显卡。
具体案例:在虚幻引擎或自研引擎中,光线追踪的反射与全局光照结果先写入低采样率 GBuffer,再通过 NVIDIA OptiX 降噪器配合法线、深度特征完成去噪。玩家转动视角时,运动矢量驱动历史帧重投影,保证了动态画面的流畅与干净。
建筑可视化与离线预览
阶段:辅助加速渲染数据清洗
解决的问题:建筑效果图渲染器通常需要“渐进式渲染”,等待时间从几分钟到几小时。设计师希望在交互预览阶段就看到接近最终效果的画面,而不是盯着满屏噪点调整参数。
具体案例:渲染器在预览模式下用 OIDN 做 CPU 降噪,让设计师在低采样率下快速查看光照布局;切换视角后通过时域累积保留历史渲染信息,避免每次转动镜头都从头开始重新采样。最终出图时再关闭降噪,保证最高精度。
影视虚拟制作的实时预演
阶段:数据预处理 + 模型推理
解决的问题:虚拟制片(Virtual Production)需要在 LED 墙前实时合成背景画面,光追渲染结果必须立刻呈现给导演和演员,不能有噪声和延迟。
具体案例:预演阶段使用 OptiX 降噪,把每像素 1 个样本的粗采样直接变成干净画面,让摄像机追踪与光照反馈达到接近零延迟的交互感。导演看到的是降噪后的“完成品”,而美指可在后台保留完整的光追数据用于最终精修。
Python 演示
import numpy as np def make_clean_scene(h=64, w=64, t=0.0): """生成一张模拟的干净光追画面:一个不断左移的方块。""" yy, xx = np.mgrid[0:h, 0:w] cx = w * (0.55 - 0.25 * (t % 1.0)) cy = h * 0.5 box = (np.abs(xx - cx) < 12) & (np.abs(yy - cy) < 12) scene = np.where(box, 0.8, 0.15) return scene.astype(np.float32) def render_noisy(clean, spp=4, seed=0): """模拟路径追踪:按泊松分布生成低采样噪声。""" rng = np.random.default_rng(seed) noise = rng.poisson(clean * spp) / spp return noise.astype(np.float32) def accumulate(history, current, alpha=0.2): """时域累积:新帧按 alpha 混合进历史结果。""" return alpha * current + (1.0 - alpha) * history def bilateral(img, sigma_s=2.0, sigma_r=0.1): """空域双边滤波:空间核与值域核相乘,保住边缘。""" h, w = img.shape out = np.zeros_like(img) weight_sum = np.zeros_like(img) r = int(2 * sigma_s) for dy in range(-r, r + 1): for dx in range(-r, r + 1): dist2 = dx * dx + dy * dy ws = np.exp(-dist2 / (2 * sigma_s * sigma_s)) if ws < 1e-3: continue shifted = np.roll(np.roll(img, dy, axis=0), dx, axis=1) diff = img - shifted wr = np.exp(-(diff * diff) / (2 * sigma_r * sigma_r)) weight = ws * wr out += weight * shifted weight_sum += weight out = out / np.clip(weight_sum, 1e-6, None) return out def main(): h, w = 64, 64 history = np.zeros((h, w), dtype=np.float32) for t in range(20): clean = make_clean_scene(h, w, t) noisy = render_noisy(clean, spp=4, seed=t) history = accumulate(history, noisy, alpha=0.3) denoised = bilateral(history, sigma_s=1.8, sigma_r=0.08) mse = np.mean((denoised - clean) ** 2) if t % 4 == 0: print(f"t={t:>2d} mse={mse:.5f}") if __name__ == "__main__": main()
def accumulate(history, current, alpha=0.2):对应正文中的时域累积公式,把当前帧新样本按权重混合进历史结果。def bilateral(img, sigma_s=2.0, sigma_r=0.1):双边滤波实现,同时使用空间核与值域核。ws = np.exp(-dist2 / (2 * sigma_s * sigma_s))空间高斯权重:距离越远,权重指数衰减,控制“手臂伸多长”。wr = np.exp(-(diff * diff) / (2 * sigma_r * sigma_r))值域高斯权重:颜色差异越大的像素越不被信任,保护边缘。history = accumulate(history, noisy, alpha=0.3)每帧都复用上一帧的累积结果,把有效采样数提升数倍。denoised = bilateral(history, sigma_s=1.8, sigma_r=0.08)在时域累积基础上再抹平残噪,得到最终干净帧。mse = np.mean((denoised - clean) ** 2)用均方误差评价降噪结果离真实画面有多远。
常见误区
误区很多初学者认为用历史帧就必然产生鬼影,时域降噪只适合静态场景,运动一多就不能用。
正解现代时域降噪依赖运动矢量 + 遮挡检测动态调整混合权重。运动矢量把历史内容对齐到当前帧,遮挡检测在物体被遮住时果断丢弃旧信息,因此动态场景同样适用。好比厨师跟着移动的锅走,并在菜被换掉时扔掉旧笔记。
为何常见:刚学时只见过最简单的帧混合,忽略了运动补偿和遮挡判断这两大核心组件,于是把缺陷当成整个方案的固有缺点。
误区噪声看起来就是颗粒,把它当成“信号干扰”直接加大模糊半径,认为糊掉就干净了。
正解无差别的高斯模糊会把边缘细节一并抹平,画面呈现出一种“塑料感”。双边滤波通过值域核保留边缘,但即便有边缘保护,过大的 \(\sigma_s\) 和 \(\sigma_r\) 仍会侵蚀纹理。正确做法是时域累积做主力、空域滤波做补充,而非一味加强。
为何常见:人眼对噪声的厌恶远大于对细节丢失的敏感,初学者容易采用“一模糊遮百丑”的捷径思维。
误区厂商演示视频里一句“AI 降噪”就把画面变干净了,于是认为只要传一张噪声图进去,一切自动解决。
正解OptiX 需要正确的反照率、法线、深度特征输入。特征缺失或不对齐会导致边缘渗色、暗部闪烁,甚至产生结构性伪影。工程师必须理解管线和特征语义,而不是把模型当魔法盒。
为何常见:宣传材料简化了降噪门槛,用户只看到 API 的一行调用,忽略了背后一整套 GBuffer 输出与特征对齐工作。
误区OIDN 来自 Intel,名字里又不带 GPU,就认为它只能跑 CPU,任何场景下都被 OptiX 碾压。
正解OIDN 通过 SYCL 和 OpenCL 支持 GPU 后端,在核显、低功耗设备及部分 AMD 平台上表现出色。OptiX 依赖 NVIDIA 专有硬件和框架,两者对比要看目标平台与性能预算,不能简单定论。
为何常见:把“开源”与“CPU”划等号,忽视了 OIDN 跨厂商、跨硬件的设计初衷。
误区调参时对着静态画面反复截屏对比,觉得某一组参数漂亮就正式上线。
正解实时渲染的最终衡量标准是动态时序稳定性。静态帧干净不代表播放时不会闪烁或抖动,必须用动画回放、连续帧差异图、时序 PSNR/SSIM 曲线综合验收。
为何常见:单帧对比方便直观,时间维度的瑕疵要播放才看得到,于是被懒于动态测试的开发流程自然跳过。
面试题
Q1. 蒙特卡洛路径追踪中,采样数提升到原来的 4 倍时,噪声标准差大约降至原来的多少?
- A.1/4
- B.1/2← 正确答案
- C.1/8
- D.1/16
答案:B。蒙特卡洛估计的方差与样本数成反比,标准差与样本数的平方根成反比。样本数变为 4 倍,标准差降为原来的 \(1/\sqrt{4}=1/2\)。好比蘸汤次数翻四倍,判断精度只翻一倍。
Q2. 时域降噪中,运动矢量的主要作用是什么?
- A.压缩像素的色彩信息
- B.把历史帧对齐到当前帧← 正确答案
- C.提高光源的采样数量
- D.直接替代空域滤波
答案:B。运动矢量记录了像素从上一帧到当前帧的位移,时域累积需要先按运动矢量对历史帧进行重投影,才能取到正确位置的历史颜色。没有运动对齐,历史信息就会错位并产生残影。
Q3. 双边滤波相比普通高斯滤波,额外增加了哪种约束?
- A.空间距离权重
- B.像素值差异权重← 正确答案
- C.运动方向权重
- D.深度距离权重
答案:B。普通高斯滤波只考虑空间距离,双边滤波在此基础上增加值域核,对像素颜色差异大的邻居降低权重。这样在跨越几何边缘时不会把两侧颜色混在一起,实现边缘保持。
Q4. 使用 OptiX 降噪时,以下哪个输入特征主要用于避免不同材质间的颜色渗入?
- A.噪声颜色图
- B.反照率特征← 正确答案
- C.运动矢量图
- D.光栅深度图
答案:B。反照率描述物体本身的固有色,网络看到它就能区分“暗红色球体”和“亮红色球体”的底色差异,防止噪声颜色沿像素空间渗入相邻物体。法线和深度更多用于几何边缘保护。
Q5. 时域累积中把历史帧权重调得过大,最可能出现什么现象?
- A.噪声明显增强
- B.物体拖影变长← 正确答案
- C.边界过度锐化
- D.画面亮度闪烁
答案:B。历史帧权重过大意味着当前帧信息被过度稀释,当物体运动或光照突变时,旧画面无法及时消退,就会拖出明显的残影。相反,若当前帧权重过大,则噪声会保留过多。两者需要靠遮挡检测动态平衡。
Q6. 以下哪些是 OptiX 降噪器的典型输入特征?
- A.噪声颜色图← 正确答案
- B.反照率特征← 正确答案
- C.法线向量← 正确答案
- D.材质纹理贴图
答案:ABC。OptiX 降噪网络主要的特征输入是噪声颜色、反照率、法线向量和深度图。材质纹理贴图不直接输入网络,因为反照率已经抽象了材质的固有色信息,直接传贴图会让网络学到无关细节增加推理负担。
Q7. 一套完整的实时降噪管线通常包括哪些环节?
- A.先时域累积← 正确答案
- B.后空域滤波← 正确答案
- C.只用静帧模糊
- D.特征引导重建← 正确答案
答案:ABD。实时降噪通常先用历史帧做时域累积,再用空域滤波抹平残噪,同时利用反照率、法线等特征引导重建,做到边缘保持。只用静帧模糊忽略了时间维度的信息复用,不是工程上可用的方案。
Q8. 双边滤波中,当两个像素的颜色差异很大时,值域权重会如何变化?
- A.权重变大
- B.权重变小← 正确答案
- C.权重不变
- D.权重归零
答案:B。值域核是颜色差的指数衰减函数,颜色差异越大,权重指数级变小。这正是边缘保持的原理:跨过边缘的像素互不借鉴,而同一平面上颜色相近的像素则被充分平均。
Q9. OIDN 降噪网络采用的核心结构是以下哪一种?
- A.多层全连接网络
- B.U型编码解码网络← 正确答案
- C.循环神经网络
- D.Transformer网络
答案:B。OIDN 采用 U-Net 风格的编码-解码结构,左侧下采样捕获语义信息,右侧上采样恢复分辨率,跳跃连接保留低频细节。这种结构在图像去噪任务中能兼顾感受野与细节保留,而不适合采用全连接或序列模型。
Q10. 下列哪些手段适合用来评估降噪的时序稳定性?
- A.单帧PSNR指标
- B.动画帧回放观察← 正确答案
- C.SSIM连续帧对比← 正确答案
- D.单帧像素直方图
答案:BC。时序稳定性关心画面在连续播放中是否闪烁或抖,动画帧回放是主观判断的直接手段,而 SSIM 连续帧对比可以量化相邻帧的结构相似度波动。单帧 PSNR 和直方图只反映静态画质,无法暴露时间维度的瑕疵。