# 【插值】

# 普通插值

  • 求a与b之间位置t的值。
  • t为0时候输出值为a。
  • t为1时候输出值为b。
public static float Lerp( float a, float b, float t ) => ( 1f - t ) * a + t * b;

图片插值演示:

全屏显示

# 反向插值

  • 已知value,求a与b之间的t值。
  • value等于a值时,t为0。
  • value等于b值时,t为1。
public static float InverseLerp( float a, float b, float value ) => ( value - a ) / ( b - a );

以下是一个反向插值使用例子,已知人和音源距离,求人能听见的音量。

参数A:多远处音频值为0

参数B:多远处音频值为1

参数C:人和音源的距离

结果值:人听到音量的大小(0~1)

注意:这个例子里,因为音量不能超出1也不能小于0,所以InverseLerp对结果值进行了Camp0~1

另外一个使用 inverseLerp 的例子,比如有很多不同长度的分段subsections,也知道根据分段总长度内的一个值val,求出val是哪个分段以及对应分段内的t(0~1)。

/**
 * 获取子分段里的0~1之间的t值
 * @param subsections 段长度的数组
 * @param val 0~subsections的总长度
 * @returns index: 所在段的索引 subT:所在段内的t值(0~1)
 */
public getSubsectionT(subsections: number[], val: number): { index: number; subT: number } {
    let index = -1;
    let subT = 0;
    let tempStart: number = 0;
    for (let i = 0; i < subsections.length; i++) {
        const subsection = subsections[i];
        const tempEnd = tempStart + subsection;
        subT = Math.inverseLerp(tempStart, tempEnd, val);
        if (subT >= 0 && subT <= 1) {
            index = i;
            break;
        }
        tempStart = tempEnd;
    }
    return { index: index, subT: subT };
}

全屏显示

全屏显示

# 平滑反向插值

如果想反向插值的坡度有平滑,可以使用smoothstep。

Markdown 图片

float smoothstep(float a,float b, float x)
{
    float t = saturate((x-a)/(b-a)); // saturate的作用是 clamp0~1
    return t*t*(3.0-(2.0*t));
}

# 映射

  • 比如把 -1 到 1 的值映射成 0 到 1 的值。
  • Photoshop里的色阶就使用了映射。
public static float Remap( float iMin, float iMax, float oMin, float oMax, float value ) => Lerp( oMin, oMax, InverseLerp( iMin, iMax, value ) );

# 连续帧插值平滑


// a: 当前值
// b: 目标值
// decay: 推荐 1~25 从慢到快
// dt: 时间差,单位:秒
// 指数衰减
expDecay(a,b,decay,dt)
{
    return b+(a-b)*exp(-decay*dt);
}

decay = 16;

update()
{
    a = expDecay(a,b,decay,Time.deltaTime);
}