GPU Gems 2 · Chapter 22. Fast Prefiltered Lines

用对称滤波器离线预计算距离响应,运行时通过 edge function 和两次查表高效绘制平滑线段。

学习目标

  • 能解释 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 pxline center · phase 48% · edge sampleslookup responsedistance / radius1.0intensityd lookup = 0.082滤波器复杂度在 preprocess 消化;runtime 始终是两次查表与少量算术

runtime lookup 次数不变;质量差异来自 preprocess 表和 line setup 参数。

本章主题 Fast Prefiltered Lines 的目标不是让 line shader 变成一套昂贵的通用卷积器,而是把滤波器的复杂度放进可缓存的表,把运行时问题缩减为稳定、可验证的几何距离计算。

Fast Prefiltered Lines:把昂贵卷积移到 preprocess几何与滤波器line width wsymmetric filtersupport radius Roffline preprocessconvolution by distancenormalize to d ∈ [0, 1]32-entry lookup tableruntime pixel shaderfour edge functionsmin(side) × min(end)intensity + discard运行时成本固定:少量 dot、min 和两次 1D lookup,不随滤波器复杂度增长

1. 为什么尖锐线段需要 band-limiting

理想的宽线可以理解为中心线 LL 两侧距离 w/2w/2 内的区域。它的边缘是 step function,包含很高的频率;当像素中心以离散位置采样时,线段平移一点点就可能让某个像素从 0 直接跳到 1。对于动画,这种相位跳变就表现为闪烁、断裂或粗细变化。

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 太大 → blurrytable 只存距离响应预滤波不是免费变清晰:它主动删除无法稳定采样的高频

滤波器选择应和线宽绑定:细线使用过宽的 Gaussian 往往显得软,较粗的线可接受更平滑的 Gaussian;box 对细节更直接,但响应边界也更硬。关键是把“滤波器形状、半径、归一化和表分辨率”写进资产或渲染规格,而不是只留下一个模糊的 antialias=true 开关。

2. preprocess:把二维 convolution 压成一维表

假设 filter 对中心线对称,那么把 filter 放在距离 ss 处时,卷积结果只与 s|s| 有关,不依赖线段朝向。可以把真实距离归一化成参数 dd:中心线位置对应 d=1d=1,刚好离开宽线与滤波器 support 的位置对应 d=0d=0。对一组离散距离执行 convolution,存成一个 1D luminance texture,runtime 就能用 lookup(d) 取得 intensity。

d=clamp(1distancew/2+R,0,1). d = clamp\left(1 - \frac{distance}{w/2 + R}, 0, 1\right).

表的 entries 不需要很大:32 项通常已经足够,若误差预算更严苛可提高到 64 项,再用线性采样平滑表间间隔。真正要验证的是量化误差,而不是盲目增加表大小:在 d=0d=0d=1d=1、快速变化区域和多个 filter 半径上,对比离线 reference convolution 的最大误差。

3. runtime:四条 edge function 与两次查表

线段由窗口坐标端点 (x0,y0)(x_0,y_0)(x1,y1)(x_1,y_1)、宽度 ww 定义。围绕中心线构造四条边:两条侧边限制垂直方向 support,两条端点边限制平行方向 support。每条边都可以表示成 (a,b,c)(a,b,c),对 fragment sample p=(x,y,1)p=(x,y,1) 计算:

di=dot(edgei,p). d_i = dot(edge_i, p).

如果任意 di<0d_i<0,说明 sample 在 w/2+Rw/2+R 的保守 band 之外,可直接 discard。否则用两组边分别求最近距离:

intensity=lookup(min(d0,d2))×lookup(min(d1,d3)). intensity = lookup(min(d_0,d_2)) \times lookup(min(d_1,d_3)).

第一张表处理线段两侧,第二张表处理端点。乘积是有限宽线段真实二维 convolution 的高性价比近似;它也解释了为什么只画一条“无限长”的线会在 caps 位置留下错误的硬边。

line setup:四条 edge function 覆盖宽度与端点sample (x,y)E0 / E2 · sidesE1 / E3 · endpointsshader recorddᵢ = dot(edgeᵢ, p)any dᵢ < 0 → discardside = min(d0,d2)end = min(d1,d3)lookup(side) × lookup(end)CPU 每条线算一次系数,GPU 每个 fragment 只做线性函数评估
两次 1D lookup:把二维有限线段分解成两个响应side responselookup(min(d0,d2))multiply×side · endpointfinal intensitysmooth sides + capswrite alpha / color端点不能被“无限长线”遗漏:第二次 lookup 让 caps 也参与抗锯齿

4. 线段合成与工程预算

为了让 shader 看到完整 support,实际绘制的 wide line 应覆盖 w+2Rw+2R,并在两个方向延长 w/2+Rw/2+R。这个外扩区域不是最终可见线宽;它只是把候选 fragment 交给 edge function 和 lookup。外扩不足会漏边,外扩过多则增加 overdraw,因此应该把 band 作为可测量的渲染预算。

多条线叠加时,shader 可以把 intensity 写入 alpha,让硬件 blending 处理同色线段。若所有线同色,alpha compositing 的次序通常不影响最终覆盖;但不同颜色的线段合成是 non-commutative,必须有 back-to-front 顺序或明确接受近似排序带来的局部误差。按线段 midpoint 排序是轻量启发式,却不能替代有严格透明需求时的正确排序。

三步实验:从表到完整线段

每一步只改变一个变量,记录 line width、filter radius、表版本、四个 edge coefficient、discard band、lookup 值和 GPU 时间。若结果只在某个角度失败,优先回放 screen-space edge function,不要先调颜色或 alpha。

分步1 / 3

第一步:比较滤波器与距离响应

固定 line width 和 distance phase,在 box、Gaussian、cubic 间切换。观察 support、曲线峰值和边缘柔和度,再用离线 reference 对比表项量化误差。

对称滤波器:先决定信号带宽,再决定线条外观normalized distance from line →intensity选择规则thin line → box 更锐thick line → Gaussian 更稳support 太大 → blurrytable 只存距离响应预滤波不是免费变清晰:它主动删除无法稳定采样的高频

本章小结

  • 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+Rw/2+R 的候选区域;外扩不足会漏边,外扩过多会增加 overdraw。
  • 多线同色时 blending 可以简化合成,不同颜色线段则必须正视 alpha 的非交换性和排序误差;质量、带宽、表精度与 CPU setup 应分开测量。

练习

问题 1|计算归一化距离。 线宽 w=2w=2 像素、滤波器半径 R=1R=1 像素,某 sample 到中心线的距离为 0.50.5 像素。按 d=clamp(1distance/(w/2+R),0,1)d=clamp(1-distance/(w/2+R),0,1),它的归一化距离是多少?

问题 2|解释两次 lookup。 为什么有限线段不能只使用 lookup(min(d0,d2))

问题 3|定位端点缺失。 线段长边平滑,但端点在某些角度被裁掉。你会按什么顺序排查?

名词解释

本章出现的专业名词,用大白话再讲一遍。

prefiltered line
band-limiting
symmetric filter
lookup table
edge function
conservative rasterization

资料与写作方式声明

本章以GPU Gems 2 · Chapter 22. Fast Prefiltered Lines权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…