Chapter 10: Interpolation and Sampling
本章讨论渲染中最常见的一类画面问题:连续的几何、纹理、动画曲线和光照结果进入离散像素网格之后,怎样用有限样本生成稳定图像。读完本章,读者应能定位一处闪烁、摩尔纹、锯齿或纹理接缝问题,区分它来自插值、采样密度、过滤器、mipmap 选择、抗锯齿策略,还是 UV 数据本身。
本章使用一帧斜向地面作为贯穿材料。画面中有一块带细密砖纹的平面,近处砖纹清晰,远处砖纹在相机移动时产生闪烁,地面边界有锯齿,某些砖缝沿 UV 接缝出现断裂。这个例子同时经过顶点属性插值、纹理采样、mipmap 选择、抗锯齿 resolve 和屏幕输出,足够把插值与采样放在同一条管线路径中解释。
本章的核心结论是:插值负责在已有样本之间构造中间值,采样负责用有限位置读取信号,过滤负责把一个像素覆盖范围内的信息压缩成输出值。画面稳定性取决于这三件事是否匹配同一个像素 footprint。footprint 指一个屏幕像素反投影到纹理、几何或时间域后覆盖的区域;它越大,单点读取越容易把高频细节折叠成低频错误。
后续小节会沿着同一条路径推进:先看顶点属性与纹理值如何插值,再看采样率和信号频率的关系,接着比较抗锯齿策略的成本,最后把 texture filtering、shadow map、blue noise、mipmap、anisotropic filtering 和 UV 透视校正组织成一套可复用排查顺序。
10.1 插值基本原理与分类
插值的工作对象是已经存在的离散样本。在线段上有两个端点值时,线性插值用参数 在两个值之间取一个加权结果:。当 时结果等于左端点,当 时结果等于右端点,中间位置的变化是匀速的。这个公式在图形管线里反复出现:动画时间轴上的位置过渡、顶点 shader 输出到 fragment shader 的 varying、纹理坐标在三角形内部的变化,都可以先用这条一维规则理解。
三角形内部的顶点属性插值使用重心坐标。一个片元落在三角形内部时,光栅化阶段可以计算它相对三个顶点的权重 ,并满足 。颜色、法线、UV 这类顶点属性会按 得到片元输入。这个步骤发生在 pixel / fragment shader 之前,所以 shader 看到的 v_uv 已经是光栅化阶段生成的中间值。
斜向地面例子里,四边形被拆成两个三角形。顶点 buffer 中只有四个角的 UV;屏幕上几百万个片元的 UV 都由三角形内插值得到。近处砖纹清晰时,插值本身通常已经完成了连续坐标生成;远处闪烁时,问题多半来自一个屏幕像素覆盖了大量 texel,而 shader 仍然尝试从过细的纹理层级取值。这里要先把“坐标值怎么来”和“纹理值怎么读”分开,否则排查会混在一起。
双线性插值发生在二维纹理内部。给定一个落在四个 texel 之间的纹理坐标,硬件会在横向插两次,再在纵向插一次,得到一个平滑的颜色值。它能减轻放大纹理时的块状边界,却会在缩小纹理时暴露另一个问题:一个像素覆盖多个 texel 时,只看邻近四个 texel 的信息量不足。双线性插值改变的是局部平滑性,采样密度不足造成的高频折叠需要 mipmap、各向异性过滤或更宽的过滤核处理。
三线性插值通常指 2D 纹理在两个 mipmap 层级之间再做一次线性混合。GPU 先根据纹理坐标导数估计当前片元的 LOD,再在相邻两个 mip level 中分别做 2D 线性过滤,最后按 LOD 小数部分混合两个结果。它解决的是 mip 层级切换的突变问题,代价是额外纹理读取和更柔和的视觉结果。OpenGL 语境下,sampler 的 minification filter 会把“层内线性过滤”和“层间线性过滤”作为两个独立选择;Khronos 的 Sampler Object 页面把 filtering、LOD、comparison mode、wrap mode 放在同一组采样状态中说明。
三次插值在动画曲线、曲面参数、图像重采样和部分后处理里更常见。它会利用更多邻域样本,让曲线的一阶变化更平滑。Catmull-Rom、Hermite、B-spline 等方法都属于这个方向。三次插值的优势是连续性更好,风险是过冲、振铃和边界处理成本更高。在实时 shader 中,三次过滤通常意味着更多纹理读取;在动画系统中,它更多消耗 CPU 或 GPU 的曲线求值时间。
这一节可以形成第一条判断:插值回答“当前位置的中间值怎样构造”,采样回答“哪些位置被读取”,过滤回答“覆盖范围内的多个样本怎样合成”。当地面砖纹在相机移动时闪烁,应先确认 UV 插值是否连续,再检查 texture sample 使用的 LOD 和 filter;当动画曲线在关键帧之间产生抖动,应先检查曲线插值模型、时间步长和关键帧数据。
下面的图把本章贯穿材料放入一帧的局部路径。它只覆盖纹理颜色进入片元输出的路径,暂时不展开光照、透明混合和后处理。
这条路径中,顶点属性提供离散输入,光栅化生成片元位置和插值权重,纹理单元按 footprint 选择读取方式,抗锯齿阶段再把多个空间或时间样本合成为最终像素。后续所有排查都可以回到这条路径定位责任阶段。
10.2 采样理论与奈奎斯特准则(Nyquist)
采样把连续信号转换成离散读数。在图形渲染里,连续信号可以是三角形边界覆盖函数、纹理图案、阴影边界、法线贴图扰动、时间中的相机运动,也可以是屏幕空间后处理得到的亮度变化。像素网格给这些信号提供固定的采样位置;每个像素中心、每个 MSAA 子样本、每个 TAA jitter 位置都属于采样点。
奈奎斯特准则给出一个基本边界:如果一个一维带限信号的最高频率是 ,采样频率至少达到 ,才具备无混叠重建的条件。图形中的纹理和几何边界通常没有严格带限,砖纹、棋盘格、细线、锐利阴影和 specular highlight 都包含高频成分。因此实时渲染通常通过预过滤和多样本逼近这个条件,原始纹理和几何边界很少天然满足严格带限前提。
把这个准则放回斜向地面更直观。近处一个屏幕像素可能覆盖不到一个 texel,采样频率高于纹理频率,双线性过滤能给出平滑过渡。远处一个屏幕像素可能覆盖几十个 texel,屏幕采样频率低于砖纹频率,单点采样会把密集砖缝折叠成缓慢变化的错误条纹。摩尔纹和闪烁就是这种高频折叠进入低频区域后的视觉结果。
重建滤波是采样的另一半。采样只产生离散读数,输出图像还需要把这些读数解释成连续视觉结果。最近点过滤等于使用很窄的重建核,画面锐利但阶梯明显;线性过滤使用更宽的核,画面平滑但细节降低;TAA 使用多个时间帧的 jitter 样本再做 history resolve,能提高等效采样数,但需要处理运动向量、遮挡变化和历史失效。
采样问题可以从三个量同时判断。第一是信号频率,例如砖缝密度、阴影贴图边界、法线贴图变化速度。第二是采样频率,例如屏幕分辨率、MSAA sample count、TAA jitter 序列、shadow map resolution。第三是过滤宽度,例如 mipmap footprint、PCF kernel、resolve filter、后处理边缘过滤核。稳定图像来自三者匹配:高频信号需要更高采样率,或者在进入像素网格前被预过滤到当前采样率能承载的范围。
在 shader 中,纹理 LOD 的隐式选择通常依赖屏幕空间导数。片元 shader 以相邻像素组为单位估计 dFdx(v_uv) 和 dFdy(v_uv),得到一个屏幕像素在纹理空间中跨越的范围。这个范围越大,应该读取越低分辨率的 mip level。这个机制解释了一个常见现象:同一张纹理贴在正对相机的墙面上清晰,贴在斜向地面上远处变糊或闪烁,因为像素 footprint 在 UV 空间的形状和大小都发生了变化。
vec2 footprintX = dFdx(v_uv);
vec2 footprintY = dFdy(v_uv);
vec4 baseColor = texture(baseColorMap, v_uv);
这段 GLSL 片段展示的重点是导数与隐式 LOD 的关系。texture 调用通常由硬件根据相邻片元的 UV 导数选择 mip level;footprintX 和 footprintY 可以用于 debug 输出,帮助观察远处地面为何进入更低 mip。实际项目中还可以使用 textureGrad 显式提供导数,用于程序化 UV 或特殊后处理路径,但显式导数需要保证和屏幕 footprint 一致。
采样理论在图形工程中的工程用法是建立排查顺序。看到闪烁时先判断信号频率是否超过屏幕采样能力;看到模糊时再判断过滤核是否过宽、LOD bias 是否偏大、TAA history 是否过度累积;看到边缘锯齿时判断采样对象是几何覆盖、shader 高频、透明 alpha、阴影深度,还是后处理重建。不同对象需要不同采样策略。
10.3 Aliasing 与抗锯齿策略
Aliasing 是高频信号被低频采样错误表达后的结果。在静态截图中,它表现为锯齿、摩尔纹、远处纹理条纹、阴影边界阶梯;在运动画面中,它表现为闪烁、爬行、细线忽隐忽现。抗锯齿策略的目标是让像素输出更接近像素覆盖区域内的平均结果,同时控制 shader、带宽、历史缓存和 resolve 的成本。
SSAA(Super-Sampling Anti-Aliasing)直接提高屏幕采样密度。它用更高分辨率渲染整帧,再下采样到目标分辨率。这个方法覆盖范围广,几何边缘、shader 高频、纹理高频都能受益,因为它提高了整条 shading 路径的采样数。代价也最直接:颜色、深度、G-buffer、后处理和带宽都会按超采样倍数增加。实时项目通常只在离线截图、质量档位很高的场景或局部 supersampling 中使用。
MSAA(Multi-Sample Anti-Aliasing)提高的是每个像素内的覆盖与深度样本数量。典型管线中,三角形覆盖测试和 depth/stencil 可以在多个子样本位置执行,而 pixel shader 计算经常按像素共享结果。Direct3D 的 Rasterization Rules 文档也把 MSAA 描述为通过多个 sub-sample location 减少几何 aliasing,并说明 per-pixel calculation 可以在 covered sub-pixels 之间共享。这个边界很关键:MSAA 对硬几何边缘有效,对片元 shader 内部产生的高频纹理、法线贴图、alpha test、specular 闪烁帮助有限。
FXAA(Fast Approximate Anti-Aliasing)是后处理边缘过滤。它在最终颜色图上检测高对比边缘,然后沿边缘方向做局部模糊。它的优势是部署简单、成本低、和渲染管线耦合较轻;风险是对细节和 UI 文本产生额外柔化,并且很难区分真实纹理细节和锯齿边缘。FXAA 适合预算紧张或无法改动主渲染路径的场景,但它无法恢复采样阶段已经丢失的信息。
TAA(Temporal Anti-Aliasing)把采样分摊到多个帧。每一帧给投影矩阵加入亚像素 jitter,让同一个像素在时间上覆盖不同子像素位置;temporal resolve 使用 motion vector 把上一帧历史重投影到当前帧,再与当前颜色融合。TAA 的优势是以较低单帧成本提高等效采样密度,尤其适合现代 deferred / temporal upscaling 管线。它的主要成本是 history buffer、motion vector、reprojection、clamp / rejection 逻辑和调参;常见失败表现是 ghosting、拖影、过度模糊和 disocclusion 位置的噪点。
Jitter sampling 本身只改变采样位置分布,temporal resolve 决定这些样本怎样合成。稳定 TAA 需要几类输入协同:当前颜色提供最新样本,motion vector 提供历史对齐,depth / normal / reactive mask 提供历史有效性判断,neighborhood clamp 控制历史颜色回灌范围。缺少运动向量时,静态画面仍可累积,移动物体边界会迅速出错;历史 rejection 过强时,画面稳定性下降;历史保留过强时,拖影增加。
可以用同一组维度比较几种策略。
| 策略 | 主要采样对象 | 解决效果 | 主要成本 | 典型边界 |
|---|---|---|---|---|
| SSAA | 整条 shading 路径 | 几何、纹理、shader 高频 | 分辨率、带宽、后处理成本大幅上升 | 实时预算压力最大 |
| MSAA | 几何覆盖与深度样本 | 多边形硬边缘 | 多样本 render target、resolve、带宽 | shader 高频和透明纹理仍需额外处理 |
| FXAA | 最终颜色边缘 | 屏幕空间锯齿 | 后处理一次 pass | 细节柔化,无法分辨语义 |
| TAA | 时间分布的子像素样本 | 空间 aliasing 和部分 temporal aliasing | history、motion vector、reprojection | ghosting、模糊、遮挡新区域 |
在斜向地面例子中,地面外轮廓的锯齿可以先考虑 MSAA 或 TAA;砖纹远处闪烁应先检查 mipmap、anisotropic filtering 和 TAA resolve;细小高光爬行需要检查 roughness、normal map 频率、specular AA 或 TAA clamp;透明草叶的锯齿需要 alpha-to-coverage、dither、TAA 或专门的透明 AA 路径。相同画面里可能同时存在多种 aliasing,先按信号来源分类,再选择策略。
10.4 渲染中常见采样技巧
Texture filtering 是最常用的采样控制入口。最近点过滤读取一个 texel,适合像素艺术、ID buffer、nearest depth debug;线性过滤读取邻域 texel 并加权,适合颜色纹理放大;mipmap 在缩小时提供预过滤层级;三线性过滤平滑 mip 切换;anisotropic filtering 处理斜视角下非正方形 footprint。把这些选项看成一组资源状态会更清楚:texture 提供 mip chain,sampler 决定 filter、wrap、LOD range、LOD bias,shader 的采样指令触发具体读取。
Mipmap 的核心作用是预过滤。生成 mip chain 时,较低层级把多个高分辨率 texel 合成一个 texel,等到远处地面一个像素覆盖大范围纹理时,GPU 可以读取已经平均过的低分辨率层级。这样能减少高频砖缝被单点读取折叠成低频条纹。mipmap 的主要代价是额外显存、生成成本、streaming 管理和可能的过度模糊。压缩纹理、法线贴图、粗糙度贴图的 mip 生成还需要符合各自数据语义,颜色的平均方式不能直接套到所有贴图。
Anisotropic filtering 处理 footprint 形状问题。斜向地面远处的一个像素投影到 UV 空间后,覆盖区域常常是细长形状。普通 mipmap 使用近似方形的层级,容易在一个方向过度模糊,另一个方向采样不足。各向异性过滤会沿主方向增加多个采样 probe,更接近细长 footprint 的平均结果。它通常和 trilinear mipmap 一起使用,代价是更多纹理读取;质量档位越高,远处斜面越稳定,纹理带宽压力也越高。
Shadow map sampling 处理的是深度比较信号。阴影贴图存储从光源视角看到的深度,片元采样阴影图时会比较当前深度与贴图深度。单点比较会产生硬阶梯,PCF(Percentage-Closer Filtering)会对多个邻域深度比较结果做平均,得到较软的阴影边界。这里过滤的对象是比较后的可见性结果,普通颜色贴图过滤规则只能作为类比。kernel 越大,阴影越软,采样成本越高,接触阴影也越容易变得松散。
Blue noise 用于控制误差分布。随机采样可以把规则 aliasing 变成噪声,但白噪声在低频区域也有明显能量,画面容易显脏。Blue noise 把误差更多推向高频区域,人眼更容易把它感知为细颗粒。实时渲染常用 blue noise 做 dithered transparency、soft shadow sampling、screen-space effect 采样、TAA jitter 或 stochastic LOD。它仍然需要 resolve 或 temporal accumulation,否则噪声会停留在最终图像中。
Texture LOD bias 是一个危险但常用的调参入口。负 bias 会让采样选择更高分辨率的 mip,画面更锐利,也更容易闪烁;正 bias 会选择更低分辨率的 mip,画面更稳定,也更模糊。LOD clamp 可以限制过高或过低的 mip 选择,适合特殊材质、UI、远景贴图或 streaming 还未加载完整 mip chain 的情况。调 bias 时应同时观察静态清晰度和运动稳定性,只看截图很容易得出偏锐利的错误偏好。
这一节的采样技巧可以压缩成一条检查顺序。先确认纹理资源是否有完整 mip chain;再确认 sampler 的 min / mag filter、mipmap filter、wrap、anisotropy、LOD bias;接着检查 shader 是否使用隐式导数、显式 LOD 或显式 grad;然后观察模型表面角度、UV 密度和屏幕 footprint;最后把运动画面放入 TAA 或后处理 resolve 中复查。RenderDoc、Nsight、Xcode GPU tools 等工具可作为观察入口,重点查看当前 draw call 绑定的 texture、sampler、shader 输入和 render target;最终截图只能作为现象记录。
10.5 纹理映射坐标的插值优化策略
纹理走样排查要从 UV 数据开始。UV 是从网格表面到纹理空间的映射坐标,顶点 shader 输出它,光栅化阶段在三角形内部插值它,fragment shader 用它采样纹理。UV 密度决定单位屏幕面积对应多少 texel;UV 接缝决定相邻三角形在纹理空间是否连续;UV 拉伸决定 footprint 在不同方向上的形状。纹理 filtering 再强,也只能在给定 UV 映射上工作。
透视校正插值是纹理贴图稳定的基础。屏幕空间三角形经过透视投影后,线性屏幕坐标和三维空间深度之间存在非线性关系。现代图形管线默认对 varying 做 perspective-correct interpolation:先对属性和 做校正,再在片元处恢复属性值。这样斜向地面上的砖纹会沿真实透视收缩。若使用 noperspective 这类限定,或在自写软件光栅器里直接对屏幕坐标做线性 UV 插值,纹理会出现滑动、压扁或随视角变化的扭曲。
UV 接缝需要同时检查坐标、法线和切线空间。一个 seam 位置往往拆成多个顶点,它们拥有相同 position,却有不同 UV、normal 或 tangent。颜色纹理如果在 seam 两侧像素内容连续,双线性过滤可以保持视觉连续;normal map 和 tangent space 如果不一致,光照会在 seam 处断开;mipmap 生成若没有 padding 或 dilation,低 mip 会把图集外部颜色混入 seam 附近,远处更容易露出边线。
排查斜向地面砖纹时,可以按一条固定路径走。第一步,把 base color 换成 UV debug 颜色,确认三角形内部的 UV 方向、密度和 seam 分布。第二步,显示 mip level 或 LOD heatmap,确认远处是否进入合理低 mip。第三步,把 sampler 依次切换为 nearest、linear、trilinear、anisotropic,观察闪烁和模糊的变化。第四步,冻结相机和移动相机分别观察;静态正常但运动闪烁,通常指向采样密度、LOD、TAA 或 specular 高频。第五步,检查纹理资产是否存在足够 padding、正确 mip 生成和合适压缩格式。
UV 插值优化也包含数据布局判断。大面积地面使用过高重复次数会提高纹理频率,远处更容易 aliasing;使用较低重复次数会降低频率,但近处细节不足。常见工程做法是分层:低频大图控制颜色和材质变化,高频细节图只在近处或通过距离 fade 生效,远处降低法线和 roughness 高频贡献。这样做的目标是让材质频率随屏幕 footprint 变化,让远处表面使用更低频、更稳定的细节表达。
mipmap 选择还会受到 shader 写法影响。程序化 UV、屏幕空间扭曲、parallax mapping、atlas 动态索引和条件分支都可能破坏隐式导数的稳定性。若相邻像素走向不同分支,硬件用 2x2 quad 估计导数时会得到不连续结果,LOD 可能跳变。此时可以把采样坐标计算移动到统一路径,或使用 textureGrad 提供连续导数,或在材质特性上减少远处高频分支。关键判断是导数是否代表当前像素 footprint;单个片元的局部数值只能作为辅助观察。
一个可复用的纹理走样结论可以这样表达:先修 UV 连续性和密度,再修 mipmap 与 sampler,再修 shader 导数与显式 LOD,最后用 TAA 或后处理处理剩余屏幕空间 aliasing。若先依赖 TAA 掩盖资产和采样问题,运动、遮挡变化和低帧率下会重新暴露闪烁。若只提高纹理分辨率,远处 footprint 变大时仍会采样不足,并且显存和带宽成本会上升。
最小自检任务
给定一个场景:一个带棋盘格纹理的地面从相机脚下延伸到远处。近处棋盘格清楚,远处在相机平移时出现闪烁和摩尔纹;地面外轮廓也有锯齿。当前 sampler 使用 linear,纹理没有 mipmap,抗锯齿只开了 FXAA。请判断这个画面至少包含哪两类采样问题,并给出一条不依赖特定 GPU 工具的排查与修复顺序。
答案要点
这个画面至少包含两类问题。远处棋盘格闪烁属于纹理高频信号超过屏幕采样能力后的 aliasing;地面外轮廓锯齿属于几何覆盖在像素网格上的 aliasing。FXAA 可以柔化最终颜色边缘,对远处纹理高频的帮助有限,因为纹理采样阶段已经把高频错误写入颜色图。
排查顺序应先固定相机观察静态图,再移动相机观察 temporal flicker。随后把纹理切成 UV debug 或纯色,若远处闪烁消失,说明主要问题在纹理采样而非几何边界。接着为棋盘格生成 mipmap,把 sampler 改成 trilinear;斜视角仍然闪烁或过糊时开启 anisotropic filtering。再检查 UV 重复次数和纹理频率,必要时降低远处高频细节或做距离 fade。最后处理轮廓锯齿:可选择 MSAA、TAA 或更高分辨率渲染;若继续使用 FXAA,应接受细节柔化和无法处理纹理阶段 aliasing 的边界。
关键结论是:纹理闪烁优先从 mipmap、sampler、LOD、UV 密度和 anisotropic footprint 处理;几何边缘锯齿优先从 coverage sample、resolve 和 temporal jitter 处理。同一帧里两类 aliasing 可以同时存在,修复顺序应按信号来源分开。
本章知识点总结
- 插值边界:插值在已有样本之间构造中间值,无法恢复采样阶段已经丢失的高频信息。
- 重心坐标:三角形内部的 vertex attribute 通过重心权重生成片元输入,UV、颜色和法线都经过这条路径进入 shader。
- 双线性过滤:双线性过滤平滑二维纹理的局部 texel 过渡,适合放大场景,对缩小导致的高频折叠作用有限。
- 三线性过滤:三线性过滤在两个 mip level 的过滤结果之间混合,主要减少 mip 层级切换突变。
- 采样准则:屏幕采样频率低于信号频率时,高频细节会折叠成锯齿、摩尔纹或运动闪烁。
- 像素 footprint:一个屏幕像素反投影到纹理或时间域后的覆盖范围决定应使用多宽的过滤核和哪个 mip level。
- MSAA 边界:MSAA 改善几何覆盖 aliasing,对 shader 内部纹理高频、alpha test 和 specular 高频需要额外策略。
- TAA 成本:TAA 通过 jitter 和 history resolve 提高等效采样数,同时引入 motion vector、历史拒绝、ghosting 和模糊问题。
- mipmap 作用:mipmap 是纹理缩小时的预过滤结构,用额外显存和生成成本换取远处纹理稳定性。
- 各向异性过滤:anisotropic filtering 用更多采样 probe 处理斜视角下细长 footprint,适合远处地面和倾斜表面。
- Shadow sampling:阴影采样过滤的是深度比较后的可见性结果,PCF kernel 会在软边界质量和采样成本之间取舍。
- Blue noise:blue noise 把采样误差更多推向高频区域,常与 temporal accumulation 或 resolve 配合使用。
- UV 接缝:UV seam 处要同时检查纹理 padding、mipmap 生成、法线和 tangent space 连续性。
- 透视校正:纹理坐标需要 perspective-correct interpolation 才能在斜向表面上随深度正确收缩。
- 排查顺序:纹理走样先查 UV 与 mipmap,再查 sampler 与导数,最后查 TAA、MSAA 或后处理 resolve。