Deep Read

锯齿和抗锯齿

锯齿产生的原因

锯齿(Aliasing)的根本原因是采样频率不足以还原原始信号的全部信息,在信号处理领域这叫"走样"。放到计算机图形学里,可以拆成三层来看。

连续的场景 vs. 离散的像素。 我们想渲染的 3D 场景在数学上是连续的:一个三角形的边缘是一条无限细的直线,两种颜色的交界是瞬时突变,这种突变包含了无限高的频率。而屏幕是一张离散的像素网格,光栅化(Rasterization,它在管线中的位置见 渲染流水线完全解析渲染流水线完全解析diagram.svg 完整的渲染流水线是一个高度并行、高度优化的过程。 这里按 数据流动 的顺序拆解:CPU 应用准备 → 驱动与命令提交 → GPU 前端几何处理 → GPU 后端像素处理 → 显示。   应用准备(CPU 端)     这一阶段完全在 CPU 上运行,是渲染的"备菜"阶段:把网格、纹理从慢速硬盘读进内存(RAM),解码成 GPU 能理解的原始数据结构(顶点数组、像素位图)。整个过程是串行的,受限于 I/O 和 CPU 解码速度。   数据从磁盘到 RAM 要走一条完整链路:应用程序(Unity/UE)向操作系统发出文件读取请求(如 ReadFile API),内核的文件系统驱动定位硬盘上的文件(如 Tank.fbx、Tank_Diffuse.png),通过磁盘控制器读取;数据先被读入系统文件缓存(RAM),再复制到应用进程的内存空间。 进入内存后还要解析(Parsing)成引擎可用的结构: 网格**:CPU 上的解析库(如 Assimp 或引擎自定义代码)读取 .fbx 的二进制内容,解码成顶点数组(float3 位置)就是在这张离散网格上对连续场景做采样——每个像素中心可以看作一个采样点,它判断自己是否被三角形覆盖,然后取一个单一颜色。

违反奈奎斯特-香农采样定理。 要无损还原一个信号,采样频率必须至少是信号最高频率的两倍。而理想几何边缘包含无限高频,屏幕分辨率(采样频率)却是有限的,所以无论分辨率多高都无法满足该定理。采样率不足时,捕捉不到的高频信息会"折叠"回低频域,产生原信号里并不存在的错误低频——视觉上就表现为阶梯状的像素块,也就是锯齿。

说得更直白些:光栅化时一个像素只能有一个颜色,当三角形边缘穿过某个像素,这个像素必须做"非黑即白"的决定——要么三角形色、要么背景色,无法表达"30% 被覆盖"这种中间状态。正是这种信息的强制二元化,造成了宏观上的阶梯边缘。

一句话概括:锯齿是用有限、离散的像素(采样器)去表现无限、连续的几何信息(信号)时,因采样率不足而必然产生的走样现象。

抗锯齿(Anti-aliasing, AA)

既然锯齿源于采样率不足,抗锯齿的目标就是在最终像素颜色上尽量还原被采样点错过的原始信号。主流技术分两大阵营:在渲染过程中增加采样信息,和在渲染结束后做图像处理

增加采样信息

这类是"正统"抗锯齿,通过在像素内部或更高分辨率下获取更多样本,混合出更准确的像素颜色。效果好,但硬件开销大。

SSAA(Super Sampling,超级采样)

最暴力也最有效:以远高于目标分辨率的尺寸渲染整个场景(2x SSAA 即长宽各 2 倍、共 4 倍像素),几何、着色、纹理全都按高分辨率算,再用下采样滤波器把高清图缩到目标分辨率,例如把 4 个像素平均成 1 个。

它从根源上提升了采样率,能完美解决所有类型的锯齿,但代价是着色也按数倍像素执行,性能消耗是毁灭性的,在实时渲染中已基本被淘汰。

MSAA(Multi-Sampling,多重采样)

MSAA 是对 SSAA 的关键优化,也是 URP 前向渲染管线原生支持的核心技术。它抓住一个观察:锯齿绝大多数发生在多边形边缘,而多边形内部颜色通常是平滑的。于是把"覆盖"和"着色"分开处理:

  1. 覆盖采样(Coverage):每个像素内部设置多个子采样点(2x/4x/8x),光栅化时逐个判断子采样点是否被三角形覆盖,由此得出该像素被覆盖的"比例"(4 个子采样点中 2 个被覆盖即 50%)。
  2. 着色采样(Shading):对被覆盖的像素,像素着色器只执行一次,算出一个颜色。
  3. 混合(Resolve):把这次着色结果按覆盖比例与背景混合,50% 覆盖即 0.5 × 三角形色 + 0.5 × 背景色

在 URP(Forward)里,可直接在 URP Asset 的质量设置中选择 MSAA 级别。GPU 硬件原生支持,管线会为渲染目标创建多重采样的 Render Texture,渲染结束后通过一次 Resolve 把多样本 RT 转成单样本 RT 供后续使用。

MSAA 是"空间换时间"的典型:只在锯齿最严重的几何边缘增加采样开销,而昂贵的着色仍保持每像素一次,是质量与性能的经典折中。但它管不了着色器内部高频(高光、法线贴图)或透明贴图(alpha test)产生的锯齿。

后期图像处理

这类方法在常规渲染结束后拿到一张带锯齿的成品图,用图像算法"猜"出锯齿在哪并抹平。开销小,但会引入模糊等副作用。

FXAA(Fast Approximate,快速近似)

纯图像后处理,不关心任何几何或场景信息,步骤大致是:

  1. 把渲染图转成亮度(Luma)图。
  2. 通过相邻像素的亮度对比度识别边缘。
  3. 确定边缘像素后,判断边缘方向。
  4. 沿边缘的垂直方向采样并混合(模糊),把高对比边缘平滑掉。

优点是开销极低、几乎任何硬件都能跑,且因为只认最终图像的边缘,能处理所有类型的锯齿。缺点是它本质上是定向模糊,会让整个画面轻微发糊、丢细节,对亚像素级锯齿也较无力。

TAA(Temporal,时间抗锯齿)

目前游戏中最主流的 AA 之一,核心思想是复用历史帧信息来构建更高质量的当前帧,把空间采样摊到时间轴上:

  1. 抖动(Jitter):每帧对投影矩阵施加一个亚像素级的微小随机偏移,即使相机静止,每帧采样点位置也在轻微抖动。
  2. 历史帧重投影(Reprojection):渲染当前帧时,用运动矢量(Motion Vector)算出当前像素在上一帧的对应位置。运动矢量记录了物体或相机移动导致的屏幕空间位移。
  3. 混合(Blending):把当前帧的抖动采样颜色与重投影来的历史颜色混合。随时间推移,一个像素累积了来自不同亚像素位置的采样,相当于"时间上的超级采样"。
  4. 邻域裁切(Neighborhood Clamping):为防止混入错误颜色(如物体移走后把旧背景色混到当前物体上),用颜色裁切判断历史颜色是否有效,无效则舍弃或降权。

TAA 能以较低开销实现很好的抗锯齿,尤其擅长消除动态画面里的闪烁(Temporal Shimmering)。缺点也明显:混合多帧会降低锐度(发糊);当运动矢量不准或裁切失效时,移动物体后方会出现拖影(Ghosting,鬼影)。

对比

技术核心原理缺点适用场景
MSAA几何边缘多重覆盖采样,单次着色开销中到高,无法处理着色/透明锯齿前向渲染,追求锐利高画质且性能预算充足(PC/主机)
FXAA后处理,检测亮度边缘并模糊整体发糊、丢细节,效果一般低端设备或性能紧张时的保底,通常集成在后处理栈
TAA时间累积,复用历史帧信息动态模糊、鬼影,画面偏软主流选择,尤其适合动态场景,需开启运动矢量

根本区别在于:MSAA 是在渲染过程中解决问题,FXAA 和 TAA 是在渲染结束后修补问题。