课程主讲:闫令琪 (Lingqi Yan) | UCSB B站课程链接:https://www.bilibili.com/video/BV1X7411F744
光栅化 (Rasterization) 是将矢量图形(几何描述)转换为像素图像(光栅图像)的过程。
光栅化 = 把连续的几何图形"画"成离散的像素点,是实时渲染的基础技术。
本章对应课程第 5-6 讲(深度缓冲部分对应第 7 讲开头)
1 透视投影收尾:fovY 与宽高比
MVP 变换中透视投影的视锥体由 $l, r, b, t$(近裁剪面左、右、下、上边界)定义,但实际开发中人们更喜欢用垂直视场角 (fovY) 和宽高比 (aspect ratio) 来描述(假设对称,即 $l = -r$、$b = -t$):
| 参数 | 含义 |
|---|---|
| fovY | 垂直视场角:从相机出发,可视范围在垂直方向张开的角度 |
| aspect ratio | 宽高比:$\text{aspect} = \dfrac{\text{width}}{\text{height}}$ |

fovY / aspect 与 $l, r, b, t$ 的转换(由近裁剪面的三角关系直接得出):
$$ \tan\frac{fovY}{2} = \frac{t}{|n|} \qquad\qquad \text{aspect} = \frac{r}{t} $$
即:已知 fovY 和近裁剪面距离 $n$ 可求 $t$,再由宽高比求 $r$,对称得到 $b$、$l$。
2 视口变换 (Viewport Transformation)
MVP 之后是什么
- Model transformation(放置物体)
- View transformation(放置相机)
- Projection transformation
- 正交投影:长方体 → 标准立方体(canonical cube)$[-1,1]^3$
- 透视投影:视锥体(frustum)→ 标准立方体
- 还差最后一步:标准立方体 → 屏幕
什么是屏幕
- 屏幕是像素数组
- 数组大小 = 分辨率 (Resolution)
- Raster(德语)= 屏幕,Rasterize = 光栅化 = 画到屏幕上
- 像素 (Pixel):短语 “picture element”,每个像素是一个小方块,颜色为 RGB 混合
屏幕空间定义

| 属性 | 说明 |
|---|---|
| 像素索引范围 | $(0, 0)$ 到 $(width-1, height-1)$ |
| 像素中心 | 像素 $(x, y)$ 的中心在 $(x+0.5, y+0.5)$ |
| 屏幕覆盖范围 | $(0, 0)$ 到 $(width, height)$ |
视口变换矩阵

将标准立方体 $[-1,1]^2$ 变换到屏幕空间 $[0, width] \times [0, height]$:
$$ M_{viewport} = \begin{pmatrix} \frac{width}{2} & 0 & 0 & \frac{width}{2} \\ 0 & \frac{height}{2} & 0 & \frac{height}{2} \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} $$注意:此变换与 z 无关,只在 xy 平面上进行,平移和缩放。
3 光栅显示设备 (Raster Displays)
“画到屏幕上"之前,先认识都有哪些"屏幕”。
绘图机器
- CNC 绘图机(CNC Sharpie Drawing Machine)、激光切割机等,都是用离散控制"画出"图形的设备

示波器 (Oscilloscope)
- 阴极射线管偏转电子束直接"画线",也可以创作示波器艺术(Oscilloscope Art)

阴极射线管 (CRT)
- 逐行光栅扫描 (Raster Scan):电子束从左到右、从上到下逐行扫描,同时调制强度控制明暗
- 隔行扫描(先奇数行再偶数行)是 CRT 时代的带宽优化手段

帧缓冲 (Frame Buffer)
- 为光栅显示提供的一块内存
- 显卡/显示设备直接从帧缓冲中读取像素值显示到屏幕

平板显示 (Flat Panel Displays)
| 类型 | 原理 |
|---|---|
| LCD(液晶显示) | 通过扭转液晶改变偏振方向,来阻挡或透射光线;由背光(荧光灯或 LED)照明,部分扭转实现中间灰度 |
| LED 阵列显示 | 发光二极管阵列,每个 LED 直接发光(大型户外屏) |
| 电子墨水(电泳显示) | 电场驱动带电颜料粒子移动显色,断电后仍保持画面,切换需要时间(Kindle 等) |

4 为什么用三角形
三角形是最基本的图元,在图形学中有独特优势:

| 特性 | 说明 |
|---|---|
| 最基本的多边形 | 任何多边形都可以拆分为三角形 |
| 保证共面 | 三点确定一个平面 |
| 内外定义明确 | 有明确的方法判断点在三角形内/外 |
| 插值方法明确 | 可以在顶点间进行插值(重心坐标) |
光栅化一个三角形的输入输出:

- 输入:三角形三个顶点投影到屏幕上的位置(浮点坐标)
- 输出:一组近似覆盖该三角形的像素值
核心:判断一个像素和三角形的位置关系,考虑像素中心点和三角形关系。
5 采样与判断点在三角形内
基本思想
光栅化本质上是对三角形进行采样:
- 对屏幕上的每个像素中心,判断是否在三角形内
- 定义二值函数:


采样代码
for (int x = 0; x < xmax; ++x)
for (int y = 0; y < ymax; ++y)
image[x][y] = inside(tri, x + 0.5, y + 0.5);
注意:采样位置是像素中心 $(x+0.5, y+0.5)$,不是 $(x, y)$。
叉积判断法
之前向量计算中使用叉积判断点 P 是否在三角形 ABC 内:
bool insideTriangle(float x, float y, const Vector3f* _v) {
// 对每条边 AB, BC, CA 计算叉积
// 若 P 在三条边的同侧(左侧),则 P 在三角形内
Vector3f P(x, y, 0);
// 计算三条边向量
Vector3f AB = _v[1] - _v[0];
Vector3f BC = _v[2] - _v[1];
Vector3f CA = _v[0] - _v[2];
// 计算从顶点到 P 的向量
Vector3f AP = P - _v[0];
Vector3f BP = P - _v[1];
Vector3f CP = P - _v[2];
// 叉积判断方向
float z1 = cross(AB, AP).z;
float z2 = cross(BC, BP).z;
float z3 = cross(CA, CP).z;
// 同号则在三角形内
return (z1 > 0 && z2 > 0 && z3 > 0) || (z1 < 0 && z2 < 0 && z3 < 0);
}

边界情况
当采样点恰好在三角形边上时(两个三角形共享边),需要特殊处理:
- 可以约定:点在边上算在三角形内
- 或者:只有在特定边上才算(如左边界和上边界)
- 课程采取的宽松策略:自己不作特殊处理,不覆盖也不算错

6 包围盒优化 (Bounding Box)
问题
遍历整个屏幕的所有像素效率太低,大部分像素不在三角形内。
解决方案
使用三角形的轴对齐包围盒 (AABB) 限制采样范围:

// 计算包围盒
int minX = max(0, min(v0.x, min(v1.x, v2.x)));
int maxX = min(width - 1, max(v0.x, max(v1.x, v2.x)));
int minY = max(0, min(v1.y, min(v0.y, v2.y)));
int maxY = min(height - 1, max(v0.y, max(v1.y, v2.y)));
// 只在包围盒内采样
for (int x = minX; x <= maxX; ++x)
for (int y = minY; y <= maxY; ++y)
if (insideTriangle(x + 0.5, y + 0.5, vertices))
image[x][y] = color;
对于细长或旋转的三角形,还可以使用增量三角形遍历 (Incremental Triangle Traversal) 进一步优化:从三角形最左的像素开始,每行利用上一行的结果增量推进,只遍历真正被覆盖的像素。

7 真实显示设备上的光栅化
真实设备的像素长什么样
放大看真实 LCD 屏幕(iPhone 6S、Galaxy S5),每个像素由 R、G、B 三个子像素组成,且不同厂商的子像素排列方式(像素几何)各不相同。

仔细去看,绿色像素点会比红色和蓝色要多,是由于人眼对绿色最为敏感。绿色感光元件增多,人眼视觉上会更舒服自然一些。
彩色打印则通过半调 (Half-tone) 图案(不同密度的墨点)近似表现连续颜色。

本课程的假设
- 假设像素发射一块均匀颜色的正方形光(LCD 实际并非如此,但此近似对本课程讨论已足够)
- 于是发送采样信号(每个像素一个颜色值),显示设备物理发出对应信号
把采样后的信号与连续三角形函数对比,会发现严重问题——锯齿 (Jaggies)。

8 锯齿问题 (Aliasing)
采样无处不在
采样是图形学的核心概念,无处不在:
| 场景 | 采样的对象 |
|---|---|
| 光栅化 | 采样 2D 位置(屏幕空间) |
| 摄影 | 采样图像传感器平面 |
| 视频 | 采样时间 |

什么是锯齿
光栅化后的三角形边缘呈现阶梯状(Jaggies),这是因为采样不足导致的走样 (Aliasing) 现象——“走样"是采样错误(Errors / Mistakes / Inaccuracies)的一种。
走样的常见表现

| 类型 | 描述 | 原因 |
|---|---|---|
| 锯齿 (Jaggies) | 空间中的阶梯状边缘 | 空间采样不足 |
| 摩尔纹 (Moiré) | 图像中的条纹图案(如跳过奇数行/列缩小图片) | 图像欠采样 |
| 车轮效应 (Wagon Wheel) | 运动方向看似反转(车轮倒转错觉) | 时间采样不足 |
根本原因
信号变化太快(高频),但采样太慢(低频)。
9 抗锯齿 (Anti-Aliasing)
核心思路:先模糊,再采样 (Blurring / Pre-Filtering Before Sampling)
对三角形先预滤波(模糊),再采样,边缘像素会取中间值(半红半白→粉),从而消除锯齿。

顺序很重要:
- 先滤波,后采样(正确!)→ 边缘平滑
- 先采样,后滤波(错误!)→ 得到模糊的锯齿

频域视角

频率与正余弦:$f = \frac{1}{T}$(周期 T 的倒数),任何信号都可以写成不同频率正弦/余弦的加权和。例如方波可分解为:

傅里叶变换:将信号分解为不同频率的正弦/余弦波的加权和(空间域 ↔ 频率域相互转换)

高频信号需要更快的采样:
采样是和函数的频率相关的。

- 低频信号:采样充足,可以合理重建
- 高频信号:采样不足,重建结果错误地伪装成低频信号
- 在给定采样率下无法区分的两个频率,互称为走样 (aliases)

滤波 (Filtering)
滤波 = 去掉某些频率成分。

| 滤波类型 | 作用 | 频域效果 |
|---|---|---|
| 低通滤波 | 模糊图像,去除边界,去除高频 | 保留低频,去除高频 |
| 高通滤波 | 提取边缘,去除低频 | 保留高频,去除低频 |
| 带通滤波 | 保留特定频率范围 | 去除过高和过低频率 |
高通滤波,什么是边界,当某个图像上面和下面,左边和右边,差距很多发生剧烈的变化(高频信息)。

卷积:滤波 = 卷积 = 平均
卷积:在"滑动窗口"内做逐点局部平均(Convolution = Averaging)
以信号 [1 3 5 3 7 1 3 8 6 4] 和滤波器 [1/4, 1/2, 1/4] 为例:
第一步: 1×(1/4) + 3×(1/2) + 5×(1/4) = 3
第二步: 3×(1/4) + 5×(1/2) + 3×(1/4) = 4
...窗口逐步滑动,得到输出序列

卷积定理:空间域的卷积 = 频域的乘法(反之亦然)

于是滤波有两种等价做法:
- 直接在空间域做卷积
- 变换到频率域 → 乘以卷积核的傅里叶变换 → 逆变换回空间域
盒式滤波器 (Box Filter):1 个像素宽度的盒式滤波器相当于低通滤波(模糊)

更宽的滤波核 = 只留更低的频率(模糊得更厉害)

采样 = 重复频率内容
采样 = 重复频率内容:采样频率越高,频率域中的副本间隔越大

走样 = 频率内容混合:欠采样(稀疏采样)导致频谱副本重叠,高频信号伪装成低频信号

如何减少走样:两种方案(反走样)
| 方案 | 做法 | 局限 |
|---|---|---|
| 1. 提高采样率 | 增大频域中副本的间距(更高分辨率的显示器、传感器、帧缓冲) | 成本高,且可能需要极高分辨率 |
| 2. 抗锯齿 | 先让频率内容变"窄”(采样前滤掉高频)模糊处理,再采样 | 需要"先滤波后采样" |

实用的预滤波:1 像素宽盒式滤波
做法:
- 用 1 像素宽的盒式模糊(低通)对 $f(x,y)$ 做卷积
- 再在每个像素中心采样

关键洞察:光栅化单个三角形时,$f(x,y) = inside(triangle, x, y)$ 在像素面积内的平均值,恰好等于三角形覆盖该像素的面积比例
- 覆盖 100% → 像素值 1(纯色)
- 覆盖 50% → 像素值 0.5(中间色)

10 常见抗锯齿技术
MSAA (Multisample Anti-Aliasing)
注意区分:“超采样抗锯齿"指 SSAA (Super Sampling Anti-Aliasing),即把像素细分为 $N \times N$ 个子像素,每个子像素各自完整渲染(深度 + 着色)再平均,着色开销约为 $N^2$ 倍。而 MSAA(多重采样抗锯齿) 是 SSAA 的高效优化版:子采样点只做可见性(覆盖率)判断,不独立着色,整像素只在中心着色一次,再按覆盖率加权混合。直接算覆盖率很难,于是用多个采样点近似 1 像素盒式滤波的效果。

步骤(子采样点仅参与覆盖判断,着色只做一次):
- 把每个像素细分为 $N \times N$ 个子采样点
- 对每个子采样点判断是否在三角形内(1 或 0),得到覆盖率
- 在像素中心(或重心)着色一次,用覆盖率对颜色加权混合,得到该像素值

最终效果:边缘像素得到 25% / 50% / 75% / 100% 等中间值,显示平滑过渡

优点:效果好,能有效消除锯齿
缺点:采样点增多带来额外的覆盖测试与颜色/深度存储开销(硬件中按采样点数存储);但着色只执行一次,开销远小于 SSAA(计算量增大约 $N^2$ 倍是 SSAA 的特征)
FXAA (Fast Approximate Anti-Aliasing)
快速近似抗锯齿:后处理技术
原理:
- 不增加采样点
- 在渲染完成后,检测图像中的边缘
- 将边缘附近的像素进行平滑混合
优点:速度快,性能开销小
缺点:可能丢失细节
TAA (Temporal Anti-Aliasing)
时间抗锯齿:利用帧间信息
原理:
- 在不同帧中,对像素内的不同位置采样(每帧采样位置不同)
- 将多帧结果混合(时间上累积)
优点:效果好,适合动态场景,且当帧开销小
缺点:可能产生鬼影(ghosting)
超分辨率与 DLSS
- 超分辨率 (Super Resolution / Super Sampling):从低分辨率图像重建高分辨率图像,本质上仍是”样本不足“问题
- DLSS (Deep Learning Super Sampling):深度学习超采样,用低分辨率渲染 + AI 推理输出高分辨率画面,是近年实时渲染的重要方向
11 复盘自测
1. fovY、宽高比与近裁剪面 $l,r,b,t$ 之间的换算关系是怎样的?
假设对称视锥($l=-r$、$b=-t$),由近裁剪面构成的直角三角形直接得到:
$$ t = |n| \cdot \tan\frac{fovY}{2} \;\Rightarrow\; b = -t $$再由宽高比 aspect = width / height = r / t:
$$ r = t \cdot \text{aspect} \;\Rightarrow\; l = -r $$故已知 fovY、aspect 和近裁剪面距离 $n$,即可完全确定 $l, r, b, t$。
2. 视口变换矩阵为什么与 z 无关?它的平移和缩放因子分别由什么决定?
- 为什么与 z 无关:视口变换的目的是把标准立方体 $[-1,1]^2$ 的屏幕坐标 (x, y) 映射到显示器像素范围 $[0, width] \times [0, height]$,而深度信息 z 已经在投影阶段处理(保留给深度缓冲用,不参与屏幕成像)。
- 平移因子:$\frac{width}{2}$、$\frac{height}{2}$ —— 把缩放后中心位于原点的图像平移到屏幕中心,使范围变为 $[0, width]$、$[0, height]$。
- 缩放因子:$\frac{width}{2}$、$\frac{height}{2}$ —— 把 $[-1,1]$ 的坐标缩放到 $[0, width]$ 或 $[0, height]$(factor = 范围跨度的一半)。
矩阵形状 $M_{viewport}$ 第三行就是 $(0, 0, 1, 0)$,原样保留 z,留给后续深度测试使用。
3. 用叉积判断点是否在三角形内的原理是什么?为什么"同号"意味着在内部?
对三角形 ABC 按统一方向(逆时针或顺时针)遍历三条边 AB、BC、CA,计算边向量与"顶点 → P"向量的叉积 z 分量:
$$ z_k = \text{cross}(\text{边}_k, \text{顶点}_k \to P).z $$- 叉积 z 分量的正负表示 P 在该边的左侧(>0)还是右侧(<0)。
- 若 P 在三角形内部,则它必然同时位于三条有向边的同侧——即三个 z 分量符号完全一致(全正或全负)。
- 若有一个 z 分量异号,说明 P 在某条边的外侧,则不在三角形内。
“同号 = 在内部"正是凸性的几何体现:三角形作为凸区域,等价于"相对于三条有向边同侧的交集”。
4. 为什么"先滤波后采样"可以消除锯齿,而"先采样后滤波"只会得到模糊的锯齿?
- 先滤波后采样(正确):原始连续信号 $f(x,y)$ 含高频(如三角形边缘的阶跃)。先做低通滤波(1 像素宽盒式模糊),把超过采样极限的高频成分提前滤掉,让剩余频率满足 Nyquist 条件,再采样就不会有走样;边缘像素自然落到 0–1 之间的中间值,呈现平滑抗锯齿效果。
- 先采样后滤波(错误):先以不足的频率对原始高频信号采样,此时高频已经走样为低频错误信号,信息已经不可挽回地丢失了。再对这种含错误伪频的离散信号做低通滤波,只会让"假低频成分"和真实信号一起被模糊,得到的是仍然有锯齿轮廓的模糊图像——锯齿没消除,反而丢了清晰度。
核心:走样发生在采样瞬间,必须在采样之前滤掉高频才能预防走样;采样之后再滤波无法"撤销"已经发生的走样。
5. 采样 = 重复频率内容、走样 = 频率内容混合——这两句话如何理解?
- 采样 = 重复频率内容:从频域看,等距采样相当于用原信号频谱去乘一个冲激串(Dirac Comb)。根据卷积定理,频域的乘法对应频谱在采样频率 $f_s$ 的整数倍处周期性复制——即在频域上产生无数个原谱副本,间距正好为 $f_s$。采样频率越高,副本之间间隔越大。
- 走样 = 频率内容混合:当采样频率不足($f_s$ 太小),相邻副本之间发生重叠,原本属于高频的信息被"折叠"进低频区域,无法与真正的低频信号区分——即高频信号伪装成低频,这就是走样(aliases)。反映在图像上就是锯齿、摩尔纹等伪影。
Nyquist 采样定理的本质:要让副本不重叠,采样频率必须 $f_s \geq 2 f_{\text{max}}$,否则高频与低频混叠。
6. MSAA、FXAA、TAA、DLSS 各自的原理、优缺点和适用场景分别是什么?
| 技术 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| MSAA | 每个像素细分为 $N \times N$ 子采样点,对每个子采样点判断 inside(triangle),按覆盖率取平均,近似 1 像素盒式预滤波 | 效果好、原理清晰、几何边缘平滑 | 覆盖测试与颜色/深度存储开销随采样点数增长,但着色只执行一次、开销远小于 SSAA;对 shader 与纹理走样无效 | 几何边缘抗锯齿、传统引擎默认 AA |
| FXAA | 后处理:渲染完成后检测图像边缘,对边缘像素做颜色平滑混合 | 速度快、开销小、与场景复杂度无关 | 可能丢细节、对高对比边缘易糊、不解决时域走样 | 移动端、性能受限平台、快速近似抗锯齿 |
| TAA | 利用帧间相关性:每帧在像素内取不同位置采样(抖动 jitter),多帧在时间上累积平均,等效"高采样率” | 当帧开销小、效果优于 MSAA、可处理 shader/透明走样 | 动态场景可能产生鬼影 (ghosting)、依赖运动矢量、对快速运动敏感 | 现代实时引擎主流(UE、Unity HDRP) |
| DLSS | 深度学习超采样:以低于输出分辨率渲染,再用神经网络根据历史帧 + 运动矢量 + 输入帧重建高分辨率画面 | 性能提升显著、远超传统 TAA 清晰度、必要时甚至超过原生渲染 | 依赖 AI 硬件(Tensor Core)、可能出现伪影、不同版本(1/2/3)质量差异大 | RTX 显卡、支持 AI 加速的实时渲染 |
对比思路:
- MSAA 走的是"真正增加采样点"的路线,解决空间域走样。
- FXAA / TAA 都走"后处理 + 信息复用"的路线,靠算法/历史帧补足样本不足。
- DLSS 进一步引入"AI 先验",不局限于像素操作,而是从图像到图像的学习映射,成为超分辨率方向的新基准。
本文是 GAMES101 - 现代计算机图形学学习系列的第 4 篇笔记。