插值
本篇位置:模块一 · 第 6 篇 | 前置:四元数与旋转 | 预计阅读:30 分钟
学习目标
- 熟练使用 lerp/smoothstep 及其变体,知道它们各自的导数特性适合什么场景。
- 手推双线性插值(bilinear interpolation),写出 4 邻点权重公式并能验证权重和为 1。
- 把"图像放大"理解成"用重建核(reconstruction kernel)做重采样",而不是一个孤立的滤镜操作。
- 说清时域插值(插帧)与空间插值(超分)在数学上是同一件事——都是"用已知样本重建未知位置的值"。
lerp:一切插值的原子
上一篇的收尾落在四元数的 slerp/nlerp——两个姿态之间该怎么走。这一篇要把视角拉得更宽:不只是旋转,位置、颜色、透明度、纹理坐标、深度值,几乎所有在图形管线里出现的量,插值需求最终都能拆解回同一个最原子的操作——线性插值(linear interpolation,简称 lerp):
、 是两个已知端点的值, 是插值参数, 取到 , 取到 ,中间线性过渡。这个公式简单到几乎不需要解释,但重要性恰恰在于"到处都是它":GLSL 内建函数 mix(a, b, t) 就是它的直接实现,顶点着色器插值颜色/纹理坐标/法线是它,关键帧动画求中间姿态、色彩渐变、上一篇矩阵/四元数插值的底层运算也全是它——后面的双线性插值、三线性插值乃至更高阶的重建核,本质上都是 lerp 的嵌套或加权组合,理解了这一条公式,后面大多是"往上叠层"而非"另起炉灶"。
但纯 lerp 有一个工程上很扎眼的缺点:它是分段线性的,在 和 处的斜率(也就是变化速度)和端点外的常量区间并不匹配——一个物体如果先静止、再匀速 lerp 平移、再静止,起步和刹车的瞬间速度是突变的,这在动画里看起来会顿挫、不自然。缓动函数(easing function)里最常用的 smoothstep 就是为了修掉这个问题:
它满足 、,和 lerp 一样是端点固定的过渡曲线,但求导会发现 ——两端的斜率恰好是零,意味着运动在起点和终点都是"零速度切入、零速度切出",衔接前后的静止状态时不会有速度的跳变,这正是它比直接 lerp 顺滑的代数原因。更进一步的 smootherstep()不仅一阶导数在端点为零,二阶导数(加速度)在端点也为零,过渡感更细腻,代价是多了两次乘法。
图:lerp 是一条严格的直线,端点斜率与中段完全一样;smoothstep 在两端被"压平"成水平切线,衔接静止状态时不会有速度突变;smootherstep 在此基础上进一步压平了两端的曲率,视觉上过渡最柔和,但多付出的计算量在大多数场景里可以忽略不计。
双线性插值
lerp 只处理一维——两个端点之间插一个值。图形学里最常见的重采样场景其实是二维的:一张纹理是一个像素网格,任意一个不落在整数坐标上的采样点 ,都需要从周围的网格点"猜"出一个值,这就是双线性插值(bilinear interpolation)要解决的问题。做法是把一次二维问题拆成两次一维 lerp:取采样点所在格子的 4 个整数邻点 (下标分别是横纵方向的 0/1,对应格子的左下、右下、左上、右上),记 为 在格子内的小数部分(到左下角的相对偏移)。先沿 方向各做一次 lerp,把上下两条边分别压成一个值:
再沿 方向对这两个结果做第三次 lerp:。把三次 lerp 展开合并,就得到一个更常用的等价形式——4 邻点直接加权求和,每个邻点的权重 是两个方向线性因子的乘积:
四个权重的下标离采样点越近、权重越大,且恒有 ——这是双线性插值本质上是加权平均、不会产生超出邻点范围的值的原因。落笔前我用 Python 做了数值验证:取 ,四个邻点权重分别是 ,求和严格等于 ;用邻点值 代入,加权求和公式与"先沿 再沿 做三次 lerp"两种算法都给出 ,并且在 20000 组随机 与随机邻点值上把两种算法的差异控制在浮点误差量级(约 ),确认展开式和嵌套 lerp 的写法是同一个公式的两种等价表达,不存在写岔的空间。
这条公式在 GPU 上有一个特殊待遇:纹理采样器设成 GL_LINEAR 之后,双线性插值是纹理单元(texture unit)里的固定功能硬件电路直接算出来的,一次采样指令的延迟里就顺带做完了 4 次取值、2 次横向 lerp、1 次纵向 lerp,几乎不占用可编程 ALU 的算力预算。这也是很多图像处理算法挪到 shader 里比在 CPU 上跑快得多的原因之一——不是因为 GPU 单核更快,而是双线性重采样这类操作本身就有专用硬件在"免费"帮忙,CPU 端要达到同样的效果得老老实实按上面的公式逐点做乘加。
三线性与 mipmap
纹理还有第三个维度会用到插值:细节层级(level of detail,简称 mip 层)。mipmap 是把同一张纹理提前缩小成一系列分辨率递减的版本(每一层长宽各减半),渲染时根据物体在屏幕上的投影密度挑选最合适的一层,避免远处物体因为直接采样原始高分辨率纹理而出现锯齿状的摩尔纹。但"挑选最合适的一层"往往落在两个整数层级之间的一个小数位置,这时候需要三线性过滤(trilinear filtering):先在相邻的两个 mip 层各自做一次双线性插值,拿到两个候选颜色值,再沿 mip 层级方向对这两个值做第三次 lerp——这正是"三线性"这个名字的来源,三次 lerp 里前两次在空间维度、第三次在 mip 层级维度。各向异性过滤(anisotropic filtering)则是应对纹理被斜着看(比如地面延伸到地平线)时,像素在纹理空间对应的采样区域不再是正方形而是拉长的椭圆,mipmap 的等比缩小无法照顾这种方向性拉伸,需要沿椭圆长轴多采几个点再平均——具体怎么选点、代价如何,留到 M2 图形管线篇细讲,这里只需要知道它解决的是 mipmap 各向同性缩放这个假设失效的问题。
换个视角:插值 = 用核重建信号
前面所有的 lerp、双线性、三线性,都可以用一个更统一的框架去理解:给定一组离散样本,插值本质上是在用一个重建核(reconstruction kernel)对这些样本做加权求和,核的形状决定了权重怎么随距离衰减。最近邻(nearest)用的是一个宽度为 1 的方波核(box kernel)——只认离采样点最近的那一个样本,权重非 1 即 0;双线性用的是三角形核(tent kernel)——权重随距离线性衰减到 0,这正是上一节 公式的几何图像;双三次插值(bicubic)常用的 Catmull-Rom 核则是一段分段三次多项式,形状比三角形更接近钟形,但在离采样点稍远处会略微探到 0 以下(负值波瓣)。
图:左图是三种核的形状——box 核是硬边的矩形,tent 核是线性的三角形,Catmull-Rom 核在中心更陡峭、两侧带有细小的负值波瓣;右三图是同一张 8x8 测试图(斜边加一个孤立像素点)分别用三种插值放大后的效果,nearest 保留硬边但呈块状,bilinear 把边缘和孤立点都抹得发糊,bicubic 的边缘最锐利,孤立点周围也能看出轻微的过冲。
Catmull-Rom 核那一点负值波瓣不是缺陷,而是为了在保持锐利边缘的同时逼近理想 sinc、核在 需要负瓣带来的代价:核越接近理想 sinc,重建出来的边缘就越锐利,但这样的核在频域上对应更宽的通带,容易在阶跃状的边缘旁边产生过冲(overshoot),也就是常说的振铃(ringing)——图里孤立像素点放大后周围那一圈若隐若现的亮边就是这个效应的直接体现。这也解释了图像放大算法之争为什么本质上是一场"核之争":nearest 完全不模糊但有块状锯齿,bilinear 平滑但发糊丢细节,bicubic/Lanczos 更锐但有振铃风险,往上还有更复杂的核(比如 Lanczos 用 sinc 函数截断),每一种放大算法本质上都是在核形状的"锐利 vs 振铃 vs 计算代价"这个三角权衡里选一个点,没有免费的最优解,只有针对具体内容特征的取舍。
时域插值:插帧的数学骨架
前面几节的插值都发生在空间维度——同一时刻、不同像素位置之间。插帧(frame interpolation)要做的事情本质相同,只是把插值的自变量从"空间坐标"换成了"时间":给定两帧真实渲染或解码出的图像 、,要在它们中间造出一个时刻 的中间帧 。最基础的运动补偿(motion-compensated)插帧公式是:
逐个符号看: 是中间帧里要计算颜色的像素坐标; 是运动矢量(motion vector),描述这个位置的内容从 到 之间的位移,通常由光流估计(optical flow)或运动估计(motion estimation)预先算出; 假设内容按 做匀速运动,从中间帧往回倒 这么长的时间比例,对应 里该采样的位置; 是同样的道理往前看,对应 里的位置。两次采样(通常还要借助前几节的双线性/双三次重建,因为落点往往不是整数像素)各得到一个候选颜色,最后用 对两者做一次 lerp——这就是"双向 warp"(bidirectional warp)的简化式:既向前又向后 warp,再按时间比例混合,而不是只信任一份。
这条公式在一个隐藏假设下才成立:运动矢量 真实、唯一,且该像素位置在两帧里都"存在"。遮挡(occlusion)与去遮挡(disocclusion)恰好打破这个假设,是插帧伪影最主要的来源。设想一个物体在两帧之间发生相对运动,挡住了背景的一部分(occlusion, 可见的区域在 里被前景挡住、找不到对应),或者相反——移动让原本被挡住的背景露了出来(disocclusion, 被挡住、 才出现的区域)。这两种情况下,"这块区域的运动矢量"本身就没有良定义的答案:disocclusion 区域在 里不存在真实对应像素,运动估计只能外推或猜测一个 , 采到的其实是 里一块不相关的内容;occlusion 区域反过来在 里找不到落点。用一个不可信的 代入公式,lerp 出来的就是两份不该混在一起的内容——视觉上表现为运动物体边缘的鬼影(ghosting,前一帧残影叠在新内容上)或撕裂状错位,尤其在轮廓、快速运动的前景边缘最明显。工程上的应对是先做遮挡检测(比如对前向、后向光流做一致性校验,不一致的区域标记为不可信),再对这些区域单独处理——用单向信息补洞或做空洞填充(hole filling)——而不是无脑套用这条公式,这也是插帧工程实现里公式之外最费工夫的部分。
空间插值:超分的起点
把视角转回空间维度。图像超分辨率(super-resolution)要解决的问题,从最基础的版本看,和"给纹理放大"是同一件事:已知一张低分辨率图,要在更密的像素网格上估计出值——bilinear、bicubic 放大正是所有超分算法共同的起点(baseline)。经典的一代做法是"先插值、再锐化":用 bicubic 之类的核把图放大,因为核本身有低通滤波的效果,放大后的图会偏糊,于是再叠加一次反锐化蒙版(unsharp mask)或更精细的边缘增强,人为地把高频细节找补回来——这类方法的天花板很明确,因为插值阶段本来就没有引入任何插值前不存在的真实细节,锐化只是在增强对比度、制造"看起来更清晰"的错觉,并不能凭空生成被降采样丢掉的信息。现代超分辨率算法大致能分成三条路子,容易被混为一谈但取舍并不相同。Lanczos 仍然属于上一节的固定核家族——用 sinc 函数截断出的一个解析核,核形状不随图像内容变化,只是比 Catmull-Rom 用了更宽的截断窗口。FSR2 这类走的是另一条路:核(或权重)会依据边缘方向、历史帧置信度等信号手工设计地做内容/时域自适应,但调整规则是工程师写死的启发式,不是从数据里学出来的。真正意义上的"学习型核"(learned kernel)留给基于深度学习的 EDSR、ESRGAN、DLSS 一类:与其用固定形状或手工规则的核,不如通过大量真实高低分辨率图像对训练出一个随位置自适应的核,在边缘处收窄、变陡峭以保留锐度,在平坦区域放宽、变平滑以抑制噪声。三者本质上仍然是"用核对已知样本做加权重建",区别只在于核的形状是固定解析式、手工启发式,还是数据驱动学出来的。
🎯 岗位关联
这一篇是本系列里岗位数学关联最直接的一篇:插帧的核心就是上一节的时域 lerp 加运动补偿公式,超分辨率的 baseline 就是这一节讲的空间重采样——理解了 、双线性权重、重建核这三层,再去看任何一篇插帧或超分的论文,公式部分都不会陌生。遮挡/去遮挡伪影的成因也是实际排查画面鬼影问题时最常用到的一条诊断思路:先怀疑运动矢量在物体边缘是否失真,再怀疑遮挡区域有没有做特殊处理,往往比盲目调参数更快定位问题。后续 M4(Shader 编写)会把本篇的 lerp、双线性权重、时域插帧公式直接写成 GLSL/compute shader;M5 capstone(Layer 机制)会把这套插帧或超分逻辑封装成后处理层,注入到真实渲染管线的输出之后。
小结
插值方法速查表
| 方法 | 核 | 代价 | 质量特征 |
|---|---|---|---|
| lerp | — | 1 次乘加 | 端点斜率不连续,动画顿挫 |
| smoothstep | — | 多几次乘法 | 端点一阶导为零,过渡顺滑 |
| nearest | box(方波) | 1 次取值 | 无模糊但有块状锯齿 |
| bilinear | tent(三角形) | 3 次 lerp / 硬件免费 | 平滑但发糊,无振铃 |
| trilinear(mipmap) | tent × 2 层 + 层间 lerp | 双线性的两倍 | 抑制远处摩尔纹,仍会糊 |
| bicubic(Catmull-Rom) | 分段三次核 | 16 邻点加权 | 更锐利,边缘有轻微过冲/振铃 |
| 运动补偿时域插值 | 双向 lerp + 重建核 | 光流估计 + 两次重建 | 遮挡区域易鬼影,需专门处理 |
| 学习型超分核 | 数据驱动、随位置自适应 | 训练/推理成本高 | 锐度与真实细节最优,但依赖模型质量 |
延伸阅读
- Inigo Quilez,smoothstep 专栏:https://iquilezles.org/articles/ismoothstep/
- Don P. Mitchell、Arun N. Netravali,Reconstruction Filters in Computer Graphics(Mitchell-Netravali 论文):https://www.cs.cornell.edu/~srm/publications/SIG88-Mitchell.pdf
- GPUOpen,FidelityFX Super Resolution 2:https://gpuopen.com/fidelityfx-superresolution-2/
