学习目标
能解释 Fast Prefiltered Lines 如何把对称滤波器与线宽转换为离线距离响应表,并说明它为何能以固定 runtime 成本支持不同滤波器
能从线段端点构造四条 edge function,使用 side/end 两次 lookup 得到有限线段强度,并判断 conservative rasterization 的覆盖范围
能调节 PrefilteredLinesLab 的 filter、line width、distance phase 和 radius,比较抗锯齿质量、模糊范围与 preprocess/runtime 预算
先把“线条锯齿”拆成信号与成本
GPU Gems 2 Chapter 22 的 Fast Prefiltered Lines 处理一个很具体的工程矛盾:数学上的线段没有面积,但显示器只能采样有限像素;如果直接把尖锐线段交给像素网格,斜率变化或动画会把高频边缘变成 jaggies。硬件线抗锯齿通常受 sample 数、窄 filter support 和 box filter 限制,不同 GPU 的结果也可能不一致。
本章的关键转换是:不要在每个像素重新做昂贵的二维 convolution,而是先假设 filter 关于线中心对称,把“滤波器放在离线不同距离处得到的响应”预计算成一个小型 1D 表。运行时只需计算 sample 到线段四条边的距离,查两次表,再把侧边响应与端点响应相乘。
Prefiltered lines 实验
调整滤波器、线宽、距离相位与 support,观察边缘强度和预处理/运行时成本的分工。
prefiltered line lab:gaussian filter / support 3.0 px line center · phase 48% · edge samples lookup response distance / radius 1.0 intensity d lookup = 0.082 滤波器复杂度在 preprocess 消化;runtime 始终是两次查表与少量算术
先画出 support,再扩大 raster band
调试时先记录 line width w w w 、filter radius R R R 、归一化距离 d d d 和 lookup
输出。随后检查保守光栅化是否生成了所有距离不超过 w / 2 + R w/2+R w /2 + R 的 fragment;若 band
太窄,shader 没有机会修复漏掉的边缘。
把提高分辨率当成唯一抗锯齿方案
现象 → 线条在静止画面稍好,但动画仍然闪烁;继续提高 framebuffer 分辨率后 GPU
成本快速上升。原因 →
尖锐线段含有无法被有限像素稳定表达的高频,分辨率只能减轻而不能消除采样误差。修法
→ 先把信号 band-limit,用可控的 symmetric filter
预滤波,再以距离响应在目标分辨率重建。
本章主题 Fast Prefiltered Lines 的目标不是让 line shader 变成一套昂贵的通用卷积器,而是把滤波器的复杂度放进可缓存的表,把运行时问题缩减为稳定、可验证的几何距离计算。
prefiltered line ↡ 在绘制前把滤波器与线段宽度的卷积响应按到中心线距离采样并存入小表,使运行时可用查表代替完整卷积的线段抗锯齿方法。
band-limiting ↡ 主动去除有限采样无法稳定重建的高频,使尖锐信号变成目标像素分辨率能够表示的带限信号。
Fast Prefiltered Lines:把昂贵卷积移到 preprocess 几何与滤波器 line width w symmetric filter support radius R offline preprocess convolution by distance normalize to d ∈ [0, 1] 32-entry lookup table runtime pixel shader four edge functions min(side) × min(end) intensity + discard 运行时成本固定:少量 dot、min 和两次 1D lookup,不随滤波器复杂度增长
1. 为什么尖锐线段需要 band-limiting
理想的宽线可以理解为中心线 L L L 两侧距离 w / 2 w/2 w /2 内的区域。它的边缘是 step function,包含很高的频率;当像素中心以离散位置采样时,线段平移一点点就可能让某个像素从 0 直接跳到 1。对于动画,这种相位跳变就表现为闪烁、断裂或粗细变化。
symmetric filter ↡ 围绕一个无穷薄中心线的滤波器响应;它通常要求左右两侧形状相同,使响应只依赖 sample 到中心线的距离。
band-limiting 的工程含义是用低通 filter 与线段信号做 convolution。滤波器的 support 至少要覆盖一个以上像素,才能让边缘跨过多个采样位置时逐渐变化;但 support 太大又会让线条明显变 blurry。理想 sinc filter 的 support 无穷大,无法直接用于实时渲染,因此通常选择 box、Gaussian 或 compact cubic 等有限 support 的近似。
对称滤波器:先决定信号带宽,再决定线条外观 normalized distance from line → intensity 选择规则 thin line → box 更锐 thick line → Gaussian 更稳 support 太大 → blurry table 只存距离响应 预滤波不是免费变清晰:它主动删除无法稳定采样的高频
滤波器选择应和线宽绑定:细线使用过宽的 Gaussian 往往显得软,较粗的线可接受更平滑的 Gaussian;box 对细节更直接,但响应边界也更硬。关键是把“滤波器形状、半径、归一化和表分辨率”写进资产或渲染规格,而不是只留下一个模糊的 antialias=true 开关。
半径变了却继续复用旧表
现象 → 调大 filter radius
后线条亮度、宽度和边缘位置不一致,某些角度还出现暗缝。原因 → lookup table
编码的是旧的 w / 2 + R w/2+R w /2 + R 支持范围,runtime 的归一化距离与表的语义已经错位。修法 →
把 line width、radius、filter family 和表版本作为同一个
key;改变任一参数都重新生成并验证常量、中心和外边界响应。
2. preprocess:把二维 convolution 压成一维表
lookup table ↡ 把采样距离或参数映射为卷积结果的紧凑数据结构;运行时用它替代对滤波器面积的逐像素积分。
假设 filter 对中心线对称,那么把 filter 放在距离 s s s 处时,卷积结果只与 ∣ s ∣ |s| ∣ s ∣ 有关,不依赖线段朝向。可以把真实距离归一化成参数 d d d :中心线位置对应 d = 1 d=1 d = 1 ,刚好离开宽线与滤波器 support 的位置对应 d = 0 d=0 d = 0 。对一组离散距离执行 convolution,存成一个 1D luminance texture,runtime 就能用 lookup(d) 取得 intensity。
d = c l a m p ( 1 − d i s t a n c e w / 2 + R , 0 , 1 ) . d = clamp\left(1 - \frac{distance}{w/2 + R}, 0, 1\right). d = c l am p ( 1 − w /2 + R d i s t an ce , 0 , 1 ) .
表的 entries 不需要很大:32 项通常已经足够,若误差预算更严苛可提高到 64 项,再用线性采样平滑表间间隔。真正要验证的是量化误差,而不是盲目增加表大小:在 d = 0 d=0 d = 0 、d = 1 d=1 d = 1 、快速变化区域和多个 filter 半径上,对比离线 reference convolution 的最大误差。
只看表项数量,不测归一化端点
现象 → 32 项表看起来足够,但线中心发灰、外侧仍有一圈可见亮度。原因 →
表的中心值、零响应位置和 runtime clamp 没有统一,线宽或 radius
被重复缩放。修法 → 为每张表写入 d=0 应为 0、d=1
应为峰值的断言;用固定点、半点和 support 外样本逐项对照 reference。
3. runtime:四条 edge function 与两次查表
edge function ↡ 在屏幕坐标中用 $ax+by+c$ 表示样本相对一条直线的位置,并把每条线段边界的距离计算变成一次 dot product。
线段由窗口坐标端点 ( x 0 , y 0 ) (x_0,y_0) ( x 0 , y 0 ) 和 ( x 1 , y 1 ) (x_1,y_1) ( x 1 , y 1 ) 、宽度 w w w 定义。围绕中心线构造四条边:两条侧边限制垂直方向 support,两条端点边限制平行方向 support。每条边都可以表示成 ( a , b , c ) (a,b,c) ( a , b , c ) ,对 fragment sample p = ( x , y , 1 ) p=(x,y,1) p = ( x , y , 1 ) 计算:
d i = d o t ( e d g e i , p ) . d_i = dot(edge_i, p). d i = d o t ( e d g e i , p ) .
如果任意 d i < 0 d_i<0 d i < 0 ,说明 sample 在 w / 2 + R w/2+R w /2 + R 的保守 band 之外,可直接 discard。否则用两组边分别求最近距离:
i n t e n s i t y = l o o k u p ( m i n ( d 0 , d 2 ) ) × l o o k u p ( m i n ( d 1 , d 3 ) ) . intensity = lookup(min(d_0,d_2)) \times lookup(min(d_1,d_3)). in t e n s i t y = l oo k u p ( min ( d 0 , d 2 )) × l oo k u p ( min ( d 1 , d 3 )) .
第一张表处理线段两侧,第二张表处理端点。乘积是有限宽线段真实二维 convolution 的高性价比近似;它也解释了为什么只画一条“无限长”的线会在 caps 位置留下错误的硬边。
line setup:四条 edge function 覆盖宽度与端点 sample (x,y) E0 / E2 · sides E1 / E3 · endpoints shader record dᵢ = dot(edgeᵢ, p) any dᵢ < 0 → discard side = min(d0,d2) end = min(d1,d3) lookup(side) × lookup(end) CPU 每条线算一次系数,GPU 每个 fragment 只做线性函数评估
两次 1D lookup:把二维有限线段分解成两个响应 side response lookup(min(d0,d2)) multiply × side · endpoint final intensity smooth sides + caps write alpha / color 端点不能被“无限长线”遗漏:第二次 lookup 让 caps 也参与抗锯齿
只画原始宽度,忘记保守 raster band
现象 → 线中心和长边正常,但靠近端点或滤波器外缘的像素缺失,线条像被裁掉。原因
→ 硬件 rasterizer 没有生成所有位于 w / 2 + R w/2+R w /2 + R 内的 fragment,pixel shader
没有机会执行 lookup。修法 → 按 support 扩大绘制宽度和端点长度,使用
conservative band 覆盖所有可能非零的响应,再由 edge function discard。
把四个距离都相乘或取平均
现象 → 线条变得过暗,端点附近尤其明显,改变角度后亮度还不守恒。原因 →
两侧边和端点边的几何语义被混在一起,破坏了“侧向响应 ×
端点响应”的可分解近似。修法 → 先按 ( d 0 , d 2 ) (d_0,d_2) ( d 0 , d 2 ) 与 ( d 1 , d 3 ) (d_1,d_3) ( d 1 , d 3 ) 分组取
min,再分别查表并相乘;用无限长线、短线和中心样本三组 reference 做断言。
4. 线段合成与工程预算
conservative rasterization ↡ 让 rasterizer 生成覆盖线段及其滤波 support 的更宽 fragment 区域,再在 shader 中剔除零响应区域的绘制策略。
为了让 shader 看到完整 support,实际绘制的 wide line 应覆盖 w + 2 R w+2R w + 2 R ,并在两个方向延长 w / 2 + R w/2+R w /2 + R 。这个外扩区域不是最终可见线宽;它只是把候选 fragment 交给 edge function 和 lookup。外扩不足会漏边,外扩过多则增加 overdraw,因此应该把 band 作为可测量的渲染预算。
多条线叠加时,shader 可以把 intensity 写入 alpha,让硬件 blending 处理同色线段。若所有线同色,alpha compositing 的次序通常不影响最终覆盖;但不同颜色的线段合成是 non-commutative,必须有 back-to-front 顺序或明确接受近似排序带来的局部误差。按线段 midpoint 排序是轻量启发式,却不能替代有严格透明需求时的正确排序。
用 z-buffer 代替不同颜色线段的排序契约
现象 →
单色线叠加没问题,换成不同颜色的电缆或轮廓线后交叉区域出现颜色依赖顺序的脏斑。原因
→ alpha blending 对不同颜色不满足交换律,标准 z-buffer
也不能表达每个像素的完整 back-to-front 线段序列。修法 →
明确线段的排序策略;能按对象排序就先排序,不能严格排序时记录 midpoint
heuristic 的误差边界,并避免把结果宣称为顺序无关。
把滤波质量和 runtime 成本分开验收
先用 reference convolution 评估 filter、表量化和几何距离误差,再单独测 CPU
setup、fragment overdraw、两次 texture lookup 与 blending。这样才能确认“换
Gaussian 只增加 preprocess 工作”是否真的成立,而不是把保守 band 的 overdraw
混进 lookup 成本。
三步实验:从表到完整线段
每一步只改变一个变量,记录 line width、filter radius、表版本、四个 edge coefficient、discard band、lookup 值和 GPU 时间。若结果只在某个角度失败,优先回放 screen-space edge function,不要先调颜色或 alpha。
⚡ 分步1 / 3
1 2 3 重置
第一步:比较滤波器与距离响应 固定 line width 和 distance phase,在 box、Gaussian、cubic 间切换。观察 support、曲线峰值和边缘柔和度,再用离线 reference 对比表项量化误差。
对称滤波器:先决定信号带宽,再决定线条外观 normalized distance from line → intensity 选择规则 thin line → box 更锐 thick line → Gaussian 更稳 support 太大 → blurry table 只存距离响应 预滤波不是免费变清晰:它主动删除无法稳定采样的高频 上一步 播放 下一步
本章小结
prefiltered line 通过 band-limiting 把尖锐线段变成有限分辨率可稳定采样的信号,避免把完整二维 convolution 放进每个 fragment。
symmetric filter 让卷积响应主要依赖 sample 到中心线的距离;lookup table 可以在 preprocess 阶段记录 box、Gaussian 或 compact cubic 的结果。
edge function 把线段四条边表示为线性函数,runtime 用两组 min 值进行两次 lookup,再将侧向响应与端点响应相乘。
conservative rasterization 必须覆盖 w / 2 + R w/2+R w /2 + R 的候选区域;外扩不足会漏边,外扩过多会增加 overdraw。
多线同色时 blending 可以简化合成,不同颜色线段则必须正视 alpha 的非交换性和排序误差;质量、带宽、表精度与 CPU setup 应分开测量。
练习 问题 1|计算归一化距离。 线宽 w = 2 w=2 w = 2 像素、滤波器半径 R = 1 R=1 R = 1 像素,某 sample 到中心线的距离为 0.5 0.5 0.5 像素。按 d = c l a m p ( 1 − d i s t a n c e / ( w / 2 + R ) , 0 , 1 ) d=clamp(1-distance/(w/2+R),0,1) d = c l am p ( 1 − d i s t an ce / ( w /2 + R ) , 0 , 1 ) ,它的归一化距离是多少?
查看答案w / 2 + R = 2 w/2+R=2 w /2 + R = 2 ,所以 d = 1 − 0.5 / 2 = 0.75 d=1-0.5/2=0.75 d = 1 − 0.5/2 = 0.75 。这表示 sample 还在中心响应到 support
外缘之间;实际 intensity 还要经过所选 filter 的 lookup table,不能直接把 d d d
当成最终颜色。
问题 2|解释两次 lookup。 为什么有限线段不能只使用 lookup(min(d0,d2))?
查看答案d 0 , d 2 d_0,d_2 d 0 , d 2 只描述中心线两侧的垂直响应,等价于一条无限长线。有限线段还需要端点边
d 1 , d 3 d_1,d_3 d 1 , d 3 限制平行方向的响应,因此要计算 lookup(min(d0,d2)) × lookup(min(d1,d3)),才能让 caps 也平滑收束。
问题 3|定位端点缺失。 线段长边平滑,但端点在某些角度被裁掉。你会按什么顺序排查?
查看答案先检查 conservative rasterization 是否覆盖了 w / 2 + R w/2+R w /2 + R
的宽度和端点延伸;再回放四条 edge function 的符号、坐标空间和 discard
条件;随后核对端点 lookup 是否使用 d 1 , d 3 d_1,d_3 d 1 , d 3 而不是重复侧边
lookup。最后将表的零响应端点、线宽与 radius 版本同 reference convolution
对照,排除表语义错位。
名词解释 本章出现的专业名词,用大白话再讲一遍。
prefiltered line
band-limiting
symmetric filter
lookup table
edge function
conservative rasterization
资料与写作方式声明
本章以GPU Gems 2 · Chapter 22. Fast Prefiltered Lines 的权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。
原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。