看超分辨率的效果,最不该看的是静止截图。一张静止的城市街景,让任何一个像样的超分模型来放大,都是漂亮的。问题出现在角色转身、镜头横扫、准星旁的小字,以及爆炸的火花里。这些地方共同的特点是:模型能参考的信息变少了,或者变得自相矛盾。理解了这一点,就能明白为什么“露馅”总是集中在几类内容上,也能明白它们为什么很难靠更大的模型解决。
时域数据:伪影的真正来源
多数游戏超分不只看当前帧,还会借用前几帧的信息:当前低分辨率的颜色,加上引擎提供的运动向量(Motion Vector)、深度和一段历史。模型的逻辑是,“这个像素上一帧在那里,所以可以把上一帧的细节搬过来”。这个逻辑成立的前提是运动向量准确。一旦运动向量缺失、错误,或者被遮挡的区域在历史帧里根本不存在,模型就只能猜,猜错了就是鬼影、拖尾和闪烁。后面每一种伪影,几乎都是这个前提被破坏的具体形态。
高速移动:拖影与鬼影
快速转镜头时,屏幕上大多数像素都在移动,历史帧的参考价值迅速下降;被前景挡住的背景在下一帧突然暴露出来(去遮挡,Disocclusion),历史里没有它,模型只能凭空重建。表现出来是运动物体后面留下一道半透明的尾巴,或者新出现的区域先糊后清。赛车游戏里的路牌、射击游戏里快速扫过的枪线,是很典型的例子。引擎侧能做的第一件事,是保证运动向量覆盖所有会动的东西,包括蒙皮角色、骨骼动画和摄像机运动,第二件事是在镜头切换或大幅跳变时主动重置历史,宁可这一帧糊一点,也不要让上一个场景的残影带进来。
细线:闪烁与断裂
细线的问题在采样。一根屏幕上只有一个像素宽、甚至不到一个像素宽的电线,在低分辨率渲染里,这一帧被采到,下一帧可能就漏掉了。超分模型看到的是“时有时无”的信号,很难判断它究竟是存在的细线,还是噪点。于是电线、栅栏、远处的建筑轮廓会闪烁、断开,或者被抹平。引擎能帮的忙是抖动采样(Jitter)要稳定且分布均匀,让细线在几帧之内被完整覆盖;美术侧也可以避免过细的几何,或者给它们做几何上的加粗。
HUD 与文字:最不该被超分的东西
游戏 UI 是最容易被误伤的对象。血条、准星、小地图和任务文字,边缘锐利、颜色固定,而且位置几乎不动,任何时域处理都会让它们的边缘出现细微的抖动或晕染。文字更严格:一个字符只有几个像素宽,模型稍有一点“脑补”,字形就变了,玩家读不出来。所以最稳妥的办法是分层:3D 场景先渲染、超分,UI 在超分之后、以最终的目标分辨率绘制并合成。这样 UI 永远是原生清晰度,也完全不会被时域历史污染。云游戏尤其需要这一点,因为一旦 UI 与画面在服务器上被压成一张视频,客户端再想分开就来不及了,这也是超分放在云端还是设备端要认真权衡的原因之一。
粒子与半透明:没有可靠的运动向量
火花、烟雾、雨雪和魔法特效,多数是半透明的粒子。它们的问题有两个:一是半透明表面下面叠着多层内容,一个像素只能有一个运动向量,却要同时代表背景和粒子;二是粒子本身变化极快,前一帧的样子几乎没有参考价值。表现出来是特效边缘发脏、烟雾拖尾、火焰纹理被“抹成油画”。引擎侧的办法是给粒子和透明物体单独输出遮罩(Reactive Mask),告诉超分“这里不要太相信历史”,或者让它们以更低的权重参与时域累积。另外还可以把粒子放在超分之后再绘制,代价是要处理与场景深度的合成。
把这些整理成引擎与超分之间的约定
综合起来,引擎与超分之间应该有一份明确的数据约定:运动向量覆盖所有动态物体,深度和曝光信息一致,UI 与场景分层,透明和粒子给出遮罩,镜头切换时发出重置信号。这与神经渲染时代引擎仍然负责的事情是同一条思路:越是依赖 AI 补全画面,引擎越需要把“哪里可信,哪里不可信”如实告诉它。
做测试时,不妨用一张固定的画面清单逐项检查:快速横扫镜头时背景细节是否拖影,远处电线是否闪烁,小字号文字能否读清,血条边缘在移动时是否抖动,爆炸和烟雾的边缘是否发脏,以及镜头硬切后第一帧有没有残留上一场景的影子。这六项通过,比一张漂亮的静态截图更能说明超分可以上线。