为什么游戏设置里有五种抗锯齿

开学这阵子课不算满,晚上经常打打游戏——主要是战争雷霆和 P 社那几个。前两天在战争雷霆里翻渲染设置,看到「抗锯齿」那一栏点开,“啪”地掉下来一长串:
抗锯齿: 关闭 ▾
SSAA
MSAA
FXAA
SMAA
TAA 我当时就有点懵——不就是把边缘弄平滑点吗,至于整出五种花样?而且全是缩写,一个都看不懂。本着”不查明白睡不着”的劲,我去搜了搜,结果发现这背后还真有点东西,比我想的有意思。这篇就是我把它捋明白之后的笔记。
先搞清楚:锯齿到底是哪来的
我一开始以为锯齿是”屏幕不够清楚”,多花点分辨率就没了。后来发现没那么简单——锯齿是个数学问题,不是清晰度问题。
屏幕是一格一格的像素,是离散的;但游戏里一条斜边、一个圆,是连续的。用一张方格网去描一条斜线,本质上是在”采样“——每个像素只能问一次”我这块到底算啥颜色”,然后填一个色。格子放不下连续的斜线,结果就是楼梯一样的台阶。
后来我查到这事有个正经名字,叫混叠(aliasing),背后是信号处理里的采样定理:要想不丢信息地还原一个信号,采样频率 至少得是信号里最高频率 的两倍。
关键就坏在这。一条边是颜色的突变——从这个色”啪”一下跳到那个色,这种突变在频率上是无穷高的。无穷高意味着:不管你屏幕分辨率多大、采样多密,都永远不够两倍。所以——
锯齿不是没擦干净,是从原理上就采不够。你只能想办法让它不那么显眼。
想通这句,我才明白为啥会有五种方案:大家都在跟”采样不够”这同一个敌人打,只是打法、花的钱不一样。下面一个个看。
SSAA:最笨,但最讲道理
SSAA(Super-Sampling)的思路特别耿直:既然采样不够,那我就多采。
比如 2×2 的 SSAA,就是偷偷按 2 倍宽、2 倍高去渲染——一个像素的地方,实际上算了 4 个子样本,最后再把这 4 个平均成一个,填回屏幕:
我多看了两眼这个”平均”,发现它其实就是一次卷积——拿一个所有权重都相等的小核(box filter)去扫:
平均 = 低通滤波 = 把那些”过高的频率”(也就是锯齿)压下去。所以 SSAA 效果是最好的,干净、没有副作用。
代价也最实在:渲染了 4 倍的像素,等于显卡多干 4 倍的活。难怪设置里它通常排第一个,旁边没人敢长期开。我自己开了一下,帧数直接腰斩。
MSAA:只在刀刃上花钱
SSAA 的浪费在哪?它对每个像素都算了 4 遍颜色——可锯齿明明只长在边缘上啊,一大片纯色的墙面里采 4 遍纯属白干。
MSAA(Multi-Sample)就抠在这:它把”算颜色”和”算覆盖”拆开了。一个像素的颜色只算一次,但记录这个像素被几何边缘盖住了几个角(coverage)的时候,按多个采样点来记。于是只有真正横跨边缘的像素,才会去做那个多样本的平均。
SSAA:每个像素都算 N 次颜色 ← 全员加班
MSAA:只在边缘按 N 个覆盖点 resolve ← 哪有锯齿在哪花钱 便宜了一大截,几何边缘也压得挺好。它的短板我也记一下:它只管几何边缘,对于贴图内部的高频细节、透明度测试(alpha test,比如铁丝网、树叶那种镂空)就照顾不到了——那些地方该糊还是糊。
FXAA / SMAA:等画面画完了,再去抹
前面两位都是在”渲染的时候”多花力气。还有一派思路完全相反:先正常画完,再拿成品图去事后处理。 这类叫后处理抗锯齿,便宜得多,因为它只对最终那张 2D 图片动手,根本不管 3D 场景。
FXAA(Fast Approximate)是里头最快也最糙的。它先把彩色图转成亮度——
这一步我盯着看了会儿,发现就是 RGB 跟一组固定权重做点积,把三个通道压成一个亮度值(绿色权重最大,因为人眼对绿最敏感)。然后比较相邻像素的亮度差,差得大的地方就判定是边,再沿着边的方向糊一糊。
快是真快,糊也是真糊——它分不清那到底是物体的边还是贴图本来的花纹,经常把该清楚的地方一起抹了。
SMAA(Subpixel Morphological)是 FXAA 的进阶版,聪明在它不只看”这儿有没有边”,还去识别边的形状——是 L 形、Z 形还是长直线,匹配出图案后再有针对性地精确混合。所以它比 FXAA 锐利干净不少,代价也只是稍微贵一点点。这俩是性价比党的最爱。
TAA:把时间也借来当采样
最后这个 TAA(Temporal)最有意思,也是我查得最久的。前面几种都在一帧之内想办法,TAA 干脆把时间这一维也拉进来用了。
它的玩法是这样:每一帧,悄悄给摄像机加一个不到一个像素的微小抖动(jitter),让采样点在像素格子里换着位置落脚。这一帧采左上角,下一帧采右下角……单看一帧都不够,但好几帧攒起来,等效于在一个像素里采了很多点——这不就又变回超采样了吗,只不过摊到了时间上。
问题是物体和镜头都在动,上一帧那个点,这一帧跑哪去了?得把它找回来。这一步叫重投影(reprojection),我查到的时候看到一个 4×4 的矩阵乘法——当时线代刚学个开头,只能大概懂个意思:拿当前像素对应的世界坐标,用上一帧的视图投影矩阵,反算出它在上一帧画面里的位置(齐次坐标下,就是一次 4×4 矩阵乘):
找到历史像素之后,跟当前帧做个加权混合,一点点累积( 通常很小,约 0.1):
也就是说,每一帧只信当前画面一点点(10%),剩下 90% 沿用攒下来的历史。几帧滚下来,样本越攒越多,边缘就越来越平滑。
整个循环大概长这样:
flowchart LR
A["亚像素抖动<br/>jitter"] --> B["渲染当前帧"]
B --> C["重投影<br/>找回历史像素"]
C --> D["按 α 混合<br/>当前 + 历史"]
D --> E["输出这一帧"]
E -->|"作为下一帧的历史"| C TAA 几乎不怎么吃性能,静止画面里效果还特别好,所以现在新游戏基本默认就是它。它的毛病我也体会到了:一动起来就容易糊,还会拖影(ghosting)——因为你复用的是”过时”的历史像素,镜头一甩,旧信息跟不上,就拖出残影了。我截图里那台机器最后选的就是 TAA,大概也是图它省又稳。
摆在一起看
捋完一轮,我用一句话各自概括了下:
| 方案 | 思路 | 画质 | 开销 | 短板 |
|---|---|---|---|---|
| SSAA | 整张图多倍采样再平均 | 最好 | 最贵 | 帧数腰斩 |
| MSAA | 只在几何边缘多采样 | 好 | 较贵 | 管不了贴图/镂空 |
| FXAA | 成品图上检边再糊 | 一般 | 极省 | 容易糊 |
| SMAA | 检边 + 识别形状再混合 | 较好 | 省 | 比 FXAA 略贵 |
| TAA | 跨帧抖动累积 | 好(静止) | 省 | 动起来拖影 |
放一起才看明白:没有谁绝对最好,全是在画质、性能、副作用之间做取舍。 卡顿就退到 FXAA/SMAA,显卡有富余又想要干净就上 MSAA/SSAA,图省心就 TAA。
写在最后
本来只是打游戏顺手点开个设置,没想到查着查着,把混叠、采样定理、卷积、还有线代里那个矩阵乘法都串了一遍——这些名词原来不是孤零零摆在课本里,是真在我屏幕上每一帧地干活。
当然我这理解还很表面:采样定理的严格证明、卷积核到底怎么设计、TAA 那套重投影和拒绝历史(防拖影)的细节,我都还没真啃下来,矩阵那块也才学个皮毛。这篇大概率有不准的地方,等以后学深了再回来改。
但有个感觉先记下来:很多看着唬人的缩写,扒开都是同一个朴素的问题在打转。 这次是”采样不够”。先记到这。
// related