FSR2 超分原理与集成
FSR2 超分原理与集成
FSR2(FidelityFX Super Resolution 2)是一套超分(upscaling)技术:拿低分辨率的渲染结果生成高分辨率的帧,效果逼近原生渲染,同时吃到低分辨率渲染的性能红利。这篇讲清楚三件事——FSR2 需要哪些输入、它的上采样算法(Lanczos2 + 边缘处理)和 RCAS 锐化怎么工作、以及把它集成进一个自研引擎时 Motion Vector 这个坑怎么填。
把 FSR2 当黑箱:需要喂什么
FSR2 输入的是渲染分辨率(低分辨率)的一组 Buffer,输出显示分辨率(超分后)的结果。
| 名称 | 来源 | 格式 | 类型 | 必需 |
|---|---|---|---|---|
| Color buffer | Render | 应用指定 | Texture | 必需 |
| Depth buffer | Render | 应用指定(1×float) | Texture | 必需 |
| Motion Vectors | Render/Presentation | 应用指定(2×float) | Texture | 必需 |
| Reactive mask | Render | R8_UNORM | Texture | 可选 |
| Exposure | 1×1 | R32_FLOAT | Texture | 可选 |
三个必需 Buffer:
- Color Buffer:当前帧渲染结果。前向渲染和延迟渲染下都是指光照计算后的场景颜色,交给 FSR2 做超分。
- Depth Buffer:Color Buffer 渲染过程中得到的深度 Buffer,辅助后续超分。
- Motion Vector:当前帧和上一帧的位移信息。直觉上是两帧的像素数据差——计算时把上一帧的变换矩阵传到当前帧,两者相减就得到 Motion Vector。
两个可选:Reactive mask(反应性遮罩)针对场景半透明物体单独提供,能改善半透物体的超分效果、避免闪烁;Exposure(场景曝光)FSR2 自带自动曝光,你也可以自己算曝光值以得到正确色调。
显存开销
FSR2 需要额外分配一块 GPU 本地内存,用来存算法的中间面(surface)和跨帧持久的时间数据。这块内存在创建 FSR2 上下文时通过后端回调分配。大致量级(3840×2160、Quality 到 Ultra Performance):Working Set 约 323~448MB,其中持久内存 63~93MB。分辨率降到 1080p,Working Set 降到约 84~115MB。显存占用随分辨率大致呈 2× 线性增长——据此可以外推 8K 之类更高分辨率的用量。要完美集成,需要按运行条件预留相当于「持久内存」列的那部分显存。
算法:以 TAA 为基础
FSR2 建立在 TAA(时间抗锯齿)之上——所以 TAA 有的问题 FSR2 大概率也有,只是 FSR2 用了更多钳制手段,让 TAA 的常见毛病不那么明显。
TAA 的核心是跨帧聚合像素级样本:聚合的样本越多,抗锯齿质量越高。做法是每帧在像素内做抖动(jitter)采样——即像素内的偏移采样。抖动序列的质量(空间和时间分布是否合理)是抗锯齿/升频好坏的关键。
每个样本对新帧的贡献不同,取决于两点:它与实际目标像素的空间相关性,以及已经积累了多少信息。距离更近、时间更近的样本更重要。新样本按空间权重与历史累积像素颜色混合——同一像素上,所有样本在时间维度的贡献相等,在空间维度仍不同。
上采样:Lanczos2 与边缘处理
上采样就是把纹理放大后,用插值填充放大出来的空缺像素。抖动过的图像能得到更好的上采样效果。上采样是 FSR2 超分的主要工作,FSR2 用 Lanczos2 函数算上采样滤波器的权重。
填充空缺像素时分两种情况:
- 非边缘:采样点 P 周围的灰度值应与 P 很接近,直接加权平均(高斯滤波)即可。
- 边缘:如果边缘也用高斯滤波,会把边缘糊掉(高斯滤波的通病)。按边缘锐化的思路,要引入一个高频滤波器 F(Q) 来对边缘做上采样。拉普拉斯算子就是常用的高频滤波器:某像素周围灰度变化越小(低频)F(Q) 越小,变化越大(高频)F(Q) 越大。高频滤波的本质仍是加权,只不过权重为负(用来求差值)。
既然非边缘和边缘都能用加权表达,就能统一成一个式子——关键是权重函数 H(Q_i):Q 不是边缘时权重为正,是边缘时要包含负权重来算高频滤波器。所以最核心的问题变成:如何判断某个像素是不是边缘。
Lanczos2(Lanczos 函数取 a=2)的妙处正在于此:它在 x∈[0,1] 区间函数值大于 0,在 x∈[1,2] 区间函数值小于 0。可以用多项式拟合它,并引入一个变量 ω 来控制 x∈[1,2] 段负半轴的函数值——这就把「是否边缘」的判断和上采样权重统一到了一个可调的函数里。如果直接用高斯那个式子做上采样,就没法对每个像素做差异化策略,很容易导致振铃(ringing)。
RCAS 锐化
超分之后还要锐化让图像更清晰,FSR2 选了 RCAS。
RCAS(Robust Contrast Adaptive Sharpening)最早在 FSR 1.0 引入,是 CAS(Contrast Adaptive Sharpening)的衍生。区别在于:CAS 用简化机制把局部对比度转成可变锐度,RCAS 用更精确的机制,在削波(clip)之前尽可能求出最大的局部锐度,并内置了限制对疑似噪点做锐化的程序。RCAS 不支持 CAS 里那些缩放功能,所以应在显示分辨率下运行。
算法上 RCAS 是拉普拉斯算子的变种。对像素 Q 按拉普拉斯模板的权重计算即可,而权重 ω 根据像素周围的对比度来定:先求出邻域的最大值 Max 和最小值 Min,其中缩放因子 Scale = Display Size / Render Size。
Motion Vector:集成里最容易翻车的一步
集成 Motion Vector 本身很简单——把上一帧的变换矩阵传到当前帧、相减即可。但传错了矩阵,超分结果会出现严重的鬼影/拖影。
自研引擎里的集成步骤:
- 把 FSR2 库引用加进项目。
- 新增第二个 Render Target View(RTV),配置成数据类型 2×float 的 RenderTarget(存 Motion Vector)。
- 修改 Color Pass 的常量缓冲区,加两个数据:
PrevMVP(上一帧的 MVP 矩阵)和Jitter(抖动变量,配合 FSR2 的 jitter 函数库使用)。 - 调试验证——错误的 Motion Vector 会直接在画面上表现为拖影,肉眼可辨对错。
PrevMVP 是关键:Motion Vector 靠当前帧和上一帧 MVP 变换的差来算,缺了上一帧的 MVP 就算不出正确位移。