硬件光栅化与透视校正插值
Hardware Rasterization & Perspective-Correct Interpolation
GPU 光栅化由三角形设置与三角形遍历组成。硬件用每条边一个边函数判断采样点是否在三角形内,利用其增量性质按瓦片分层遍历,用左上规则决定恰好落在边上的像素归属,用保护带避免大多数裁剪。边函数的值恰好是子三角形面积的两倍,因此遍历顺带得到重心坐标;透视投影下需要线性插值 与 再逐像素相除,得到透视校正插值。本页还介绍 2×2 quad 与辅助像素、保守光栅化。
屏幕坐标与 quad(23.1 节)
像素中心为 。顶点经变换(含投影、不含除以 w)得 ,屏幕坐标 。像素网格分为 2×2 的 quad:为了用有限差分求纹理 LOD 所需的导数,只要 quad 中有一个像素在三角形内,整个 quad 都要做像素着色,其中在三角形外的叫辅助像素 (helper pixel)。三角形越小辅助像素占比越大,最坏情况是只覆盖一个像素的三角形需要 3 个辅助像素,这称为 quad 过度着色。这是几乎所有 GPU 的核心设计。
边函数
对过 、 的边,法线取边向量逆时针旋转 90 度 (指向三角形内),
为正半空间。采样点在三角形内或边上当且仅当三条边都有 。
- 定点化:API 通常要求屏幕顶点坐标转为定点数(如 1.14.8 位:1 位符号、14 位整数、8 位亚像素,即每像素每方向 个位置),以便一致地定义平局规则,也让内部测试更高效;在计算边方程之前完成吸附。
- 增量性:,y 方向类似,可快速对小瓦片(如 8×8)"盖章"出每像素一位的覆盖掩码。
左上规则(DirectX):三条边都 的像素总在内部;中心恰在边上时,只有当该边是上边(水平且其余边在其下方, 且 )或左边(非水平且在三角形左侧,,一个三角形最多两条左边)时才算内部。这样共享边上的像素只属于一个三角形,避免被写两次。线段可当作一像素宽的长矩形(两个三角形或加一条边方程),点画成四边形,从而复用同一套边方程硬件。
分层遍历与三角形设置
- 计算屏幕包围盒,确定与三角形重叠的瓦片。瓦片-边测试是 AABB-平面测试的二维版本:对每条边只需测由边法线决定的那个最"内"的瓦片角(对所有瓦片相同,可在遍历前确定),该角在边外则整个瓦片在外,免去逐像素测试;横移 8 像素只需加 。
- 瓦片按之字形或空间填充曲线遍历以提高一致性,可多级(如先 16×16 再 4×4)。
相比扫描线顺序,分块遍历访问像素更一致,纹理(尤其配合 mipmap 时纹理缓存复用)、深度缓冲、颜色缓冲的局部性都更好;大三角形按扫描线遍历时,行首用过的纹素到行尾早已被逐出缓存。也因此纹理和缓冲本身都按瓦片存储。
三角形设置计算三角形上的常量(边方程系数、插值相关常量等)。裁剪必须在设置之前(可能生成更多三角形):近平面裁剪总是需要(生成一或两个三角形),屏幕边缘则多用保护带裁剪——例如保护带为 x、y 各 ±16K 像素,与屏幕相交且完全位于保护带内的三角形无需真正裁剪,只处理可见瓦片;只有超出保护带又与屏幕相交的三角形才需要完整裁剪。
重心坐标与插值(23.1.1 节)
属性插值 ,,, 为子三角形面积。由于 ( 等于边长 ,投影长度为子三角形高 ),
三角形设置预先计算 免去逐像素除法,于是内部测试的副产品就给出了重心坐标。这对深度插值和正交投影是正确的,但透视下会出错(棋盘纹理会扭曲)。
透视校正:屏幕空间中可线性插值的是 与 ( 为变换后的第四分量),
例:边两端 、,、。 两端为 4 和 2,中点 3; 两端为 1 和 1/3,中点 2/3;相除得中点 ,而正交投影下是 5。
实际要插值多个属性,所以先算透视校正重心坐标。令 (设置阶段可预存 等,或把三个函数都乘以 ),
每像素算一次即可用于所有属性;它们不再与子三角形面积成正比,分母也不是常数,所以必须逐像素除法。深度 本身已经除过 ,不应用此式,而是逐顶点算 后用 线性插值——这对深度缓冲压缩也有好处。
保守光栅化(23.1.2 节)
DirectX 11 起(OpenGL 通过扩展)支持:高估保守光栅化 (OCR)(外保守)访问所有与三角形重叠或在其内的像素;低估 (UCR)(内保守)只访问完全在三角形内的像素。可用把瓦片缩小到一个像素的分块遍历实现;无硬件支持时可用几何着色器或三角形扩张实现 OCR。用途包括图像空间碰撞检测、遮挡剔除、阴影计算、抗锯齿等。
flowchart LR A["裁剪: 近平面 + 保护带"] --> B["三角形设置: 边方程与插值常量"] B --> C["分层瓦片遍历: 瓦片角测试"] C --> D["逐采样内部测试 + 左上规则"] D --> E["重心坐标与透视校正插值"] E --> F["按 2x2 quad 派发像素着色"]
如上图,光栅化是几何与像素处理之间的桥梁。
GEA 的视角:重心坐标的面积求法与透视校正(卷 II 11.3.10.6–11.3.10.7、11.4.5.3、11.4.6.1)
面积法求重心坐标
- 片元位置 可写成三个顶点的加权平均 ,权重之和为 1;点在三角形内时三个权重都为正,在外时至少一个为负(卷 II 11.3.10.6)。
- 重心坐标也叫面积坐标:从 到三个顶点的向量把三角形分成三个子三角形,权重就是子三角形面积与总面积之比。用边向量 等与 的叉积求面积:
这与本页「边函数的值恰好是子三角形面积的两倍」是同一个事实。
- 补充(GEA 公式的两个隐含条件):① 叉积取模得到的面积恒为非负,按上式算出的 不可能为负,无法像书中所说用"有负值"判断点在外部;要用有符号面积(二维下即边函数,三维下取叉积与三角形法线 的点积)。② 透视校正公式中的 必须是屏幕空间(投影后)的重心坐标;若像书中面积法那样直接用三维顶点和三维片元位置求重心坐标,得到的已经是透视正确的权重,再套 公式反而会算错。
透视校正插值
- 直接用 插值深度是错的,因为透视投影不保持距离(透视缩短)。GEA 不给推导,只给结论:对深度的倒数做线性插值再取倒数(卷 II 11.3.10.7):
- 其他属性同理,例如颜色 。直观理解:先撤销透视除法,正常插值,再把透视除法做回去。本页 RTR4 部分用 表述同一件事。
- 以 quad 为单位光栅化的目的是让像素着色器对任意着色器变量(最常见的是纹理坐标)求屏幕空间差分
ddx/ddy,以选择 mip 级别;GEA 原文写成"相邻片元三维位置的一阶差分",范围说窄了(见 纹理过滤与 Mipmap)。
保守光栅化:书中前后说法不一
- 卷 II 11.4.6.1 写道保守光栅化"从 DirectX 12 开始"可选。
- 同书 11.4.5.3 的特性级别说明则写它在特性级别 11_1 引入、从 12_1 起成为必需(表 11.2 中 11_0 及以下为"不支持")。
- 两处说法并不完全一致(一处按 API 版本、一处按特性级别),这里照录。保守光栅化的原理见本页「保守光栅化」一节。
出处
- realtime_rendering_4ed.pdf 第 23 章 Graphics Hardware
- Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 11 章 11.3.10.6–11.3.10.7、11.4.5.3、11.4.6.1 节