插值函数

本文最后更新于:2025年4月17日 晚上

线性插值函数

lerp(y1,y2,weight)=y1+(y2y1)×weightlerp(y_1, y_2, weight) = y_1 + (y_2 - y_1) \times weight

其中 weightweight 是一个在 [0, 1] 区间的实数,倒不是因为取更大的值后这个函数就无定义了,而是因为取了更大的值,这个函数就失去了我们构造它的理由,另外,CG 会限制 weightweight 的值在 [0, 1] 的范围内,超过这个范围会被留在边界 0 或者边界 1。

这里 y1y_1 被称为起点,而 y2y_2 被称为终点,lerp 函数就是取值 y1y_1y2y_2 中间的一个值。取值由 weightweight 来控制,当 weightweight 为 0.5 时,取值刚好在起点和终点之间。为了更加方便理解,可以把公式写成这种形式:

lerp(y1,y2,weight)=(1weight)×y1+weight×y2lerp(y_1, y_2, weight) = (1 - weight) \times y_1 + weight \times y_2

简单来说,lerp 函数是在 y1y_1y2y_2 之间过渡。y1y_1y2y_2 可以是一个值,也可以是一个函数。比如,我们可以在正弦函数和线性函数之间做过渡,我们先看一下正弦函数:

正弦函数曲线

再看一下最简单的线性函数 y=xy = x

线性函数曲线

在它俩之间过渡,我们只需要使用 lerp(sinx,x,0.5)lerp(\sin x, x, 0.5) 即可。当然可以调整 weightweight 参数观察不同的结果。

weight 为 0.5

weight 为 0.8

weight 为 0.2

y1y_1y2y_2 分别为两个点时,结果就是两点间的位置。

线性插值

贝塞尔曲线

当有 AABBCC 三个点且有 DD 为从 AACC 上权重为 tt 的插值,EE 为从 CCBB 上权重为 tt 的插值,点 FF 为从 DDEE 上权重为 tt 的插值,则点 FF 是以 AA 为起点、BB 为终点、CC 为控制点的二阶贝塞尔曲线上的一点。

贝塞尔二阶曲线上的点

贝塞尔二阶曲线上的点

同理,当有 AABBCCDD 四个点,EE 为从 AACC 上权重为 tt 的插值,FF 为从 CCDD 上权重为 tt 的插值,GG 为从 DDBB 上权重为 tt 的插值,且 HH 为从 EEFF 上权重为 tt 的插值,II 为从 FFGG 上权重为 tt 的插值,JJ 为从 HHII 上权重为 tt 的插值,则点 JJ 是以 AA 为起点、BB 为终点、CCDD 为控制点的三阶贝塞尔曲线上的一点。

贝塞尔三阶曲线上的点

贝塞尔三阶曲线上的点

二阶贝塞尔曲线公式推导

f(p0,p1,t)=(p1p0)×t+p0f(p_0, p_1, t) = (p_1 - p_0) \times t + p_0 可得:

D=(CA)t+AE=(BC)t+CF=(ED)t+D=(((BC)t+C)((CA)t+A)t+((CA)t+A))=(At+Bt2Ct+CA)t+CtAt+A=(t1)2A+t2B+2t(1t)CB(t)=(1t)2A+t2B+2t(1t)C,t[0,1]\begin{aligned} D &= (C - A)t + A\\ E &= (B - C)t + C\\ F &= (E - D)t + D\\ &= (((B - C)t + C) - ((C - A)t + A)t + ((C - A)t + A))\\ &= (At + Bt -2Ct + C - A)t + Ct - At + A\\ &= (t - 1)^2A + t^2B + 2t(1 - t)C\\ \\ B(t) &= (1 - t)^2A + t^2B + 2t(1 - t)C, t \in [0, 1] \end{aligned}

三阶贝塞尔曲线公式推导

f(p0,p1,t)=(p1p0)×t+p0f(p_0, p_1, t) = (p_1 - p_0) \times t + p_0 可得:

E=(CA)t+AF=(DC)t+CG=(BD)t+D\begin{aligned} E &= (C - A)t + A\\ F &= (D - C)t + C\\ G &= (B - D)t + D\\ \end{aligned}

HH 可以看作以 AA 为起点、DD 为终点、CC 为控制点的二阶贝塞尔曲线上的点,II 可以看作以 CC 为起点、BB 为终点、DD 为控制点的二阶贝塞尔曲线上的点,所以由二阶贝塞尔曲线公式可得:

H=(FE)t+E=(1t)2A+t2D+2t(1t)CI=(GF)t+F=(1t)2C+t2B+2t(1t)DJ=(IH)t+H=(((1t)2C+t2B+2t(1t)D)((1t)2A+t2D+2t(1t)C))t+((1t)2A+t2D+2t(1t)C)=(1t)3A+t3B+3t(1t)2C+3t2(1t)DB(t)=(1t)3A+t3B+3t(1t)2C+3t2(1t)D,t[0,1]\begin{aligned} H &= (F - E)t + E\\ &= (1 - t)^2A + t^2D + 2t(1 - t)C\\ \\ I &= (G - F)t + F\\ &= (1 - t)^2C + t^2B + 2t(1 - t)D\\ \\ J &= (I - H)t + H\\ &= (((1 - t)^2C + t^2B + 2t(1 - t)D) - ((1 - t)^2A + t^2D + 2t(1 - t)C))t + ((1 - t)^2A + t^2D + 2t(1 - t)C)\\ &= (1 - t)^3A + t^3B + 3t(1 - t)^2C + 3t^2(1 - t)D\\ \\ B(t) &= (1 - t)^3A + t^3B + 3t(1 - t)^2C + 3t^2(1 - t)D, t \in [0, 1] \end{aligned}

或者 JJ 也可以看作以 EE 为起点、GG 为终点、FF 为控制点的二阶贝塞尔曲线上的点,由二阶贝塞尔曲线公式可得:

E=(CA)t+AF=(DC)t+CG=(BD)t+DJ=(1t)2E+t2G+2t(1t)F=(1t)2((CA)t+A)+t2((BD)t+D)+2t(1t)((DC)t+C)=(1t)3A+t3B+3t(1t)2C+3t2(1t)DB(t)=(1t)3A+t3B+3t(1t)2C+3t2(1t)D,t[0,1]\begin{aligned} E &= (C - A)t + A\\ F &= (D - C)t + C\\ G &= (B - D)t + D\\ J &= (1 - t)^2E + t^2G + 2t(1 - t)F\\ &= (1 - t)^2((C - A)t + A) + t^2((B - D)t + D) + 2t(1 - t)((D - C)t + C)\\ &= (1 - t)^3A + t^3B + 3t(1 - t)^2C + 3t^2(1 - t)D\\ \\ B(t) &= (1 - t)^3A + t^3B + 3t(1 - t)^2C + 3t^2(1 - t)D, t \in [0, 1] \end{aligned}

阶梯插值函数

step 函数的逻辑是

1
2
3
4
5
6
step(a, x){
if(x < a)
return 0;
else
return 1;
}

阶梯插值函数

平滑阶梯插值函数

smoothstep 函数可以用来生成 0 到 1 的平滑过渡值,它也叫平滑阶梯函数。smoothstep 函数的定义是

1
2
3
4
5
float smoothstep(float a, float b, float x) 
{
x = clamp((x - a) / (b- a), 0.0, 1.0);
return x * x * (3 - 2 * x);
}

简单来说就是:

  • 在 a < b 的情况下,当 x < a 时,返回 0,当 x > b 时,返回 1,否则在 0 和 1 之间平滑过渡:

平滑阶梯插值函数

  • 在 a > b 的情况下,当 x < b 时,返回 1,当 x > a 时,返回 0,否则在 0 和 1 之间平滑过渡:

平滑阶梯插值函数

两个 smoothstep 进行减法运算可以得到一些波形图,例如 smoothstep(1, 2, x) - smoothstep(2, 3, x):

smoothstep(1, 2, x) - smoothstep(2, 3, x)

想要增加波峰的持续宽度,可以构造 smoothstep(1, 2, x) - smoothstep(3, 4, x):

smoothstep(1, 2, x) - smoothstep(3, 4, x)


插值函数
http://example.com/posts/插值函数/
作者
祭零小白
发布于
2022年2月20日
许可协议