在 Shader 编程中,lerp(线性插值,Linear Interpolation)是一个核心函数,用于在两个值之间进行平滑过渡。它是实现颜色混合、渐变、动画过渡等效果的基础工具。


1. 基本语法

float lerp(float a, float b, float t);
float2 lerp(float2 a, float2 b, float2 t);
float3 lerp(float3 a, float3 b, float3 t);
// 其他向量类型同理
  • a:起始值。
  • b:目标值。
  • t:插值因子(范围通常为 [0, 1])。
  • 返回值ab 之间的线性插值结果:a * (1-t) + b * t

2. 工作原理

  • t = 0 时,返回 a
  • t = 1 时,返回 b
  • t = 0.5 时,返回 ab 的平均值
  • t 超出 [0, 1] 范围时:结果会向两侧外推(如 t = 2 时,返回 2b - a)。

3. 常见应用场景

3.1 颜色混合

fixed4 frag (v2f i) : SV_Target {
    fixed4 colorA = fixed4(1, 0, 0, 1);  // 红色
    fixed4 colorB = fixed4(0, 0, 1, 1);  // 蓝色
    float t = i.uv.x;  // 水平方向插值因子
    
    fixed4 finalColor = lerp(colorA, colorB, t);
    return finalColor;  // 从左到右红→蓝渐变
}

3.2 基于距离的混合

float2 center = float2(0.5, 0.5);
float dist = distance(i.uv, center);  // 到中心的距离
float t = smoothstep(0.2, 0.8, dist);  // 平滑过渡

fixed4 col = lerp(_ColorA, _ColorB, t);  // 基于距离混合颜色

3.3 基于时间的动画

float t = frac(_Time.y);  // 0~1 循环的时间因子
fixed4 col = lerp(_ColorA, _ColorB, t);  // 颜色随时间变化

3.4 透明度混合

fixed4 baseColor = tex2D(_MainTex, i.uv);
fixed4 overlayColor = tex2D(_OverlayTex, i.uv);
fixed4 finalColor = lerp(baseColor, overlayColor, overlayColor.a);  // 基于透明度混合

4. 变体函数

4.1 smoothstep

实现平滑的 S 形插值(而非线性):

float smoothstep(float edge0, float edge1, float x);
  • 应用场景:创建自然过渡效果(如光晕、边缘淡化)。

4.2 mix

在某些 Shader 语言(如 GLSL)中,mix 等同于 lerp

float mix(float a, float b, float t);

5. 性能注意事项

  • 计算效率lerp 是轻量级操作,直接对应硬件指令。
  • 优化技巧:若多次使用相同的 t,可预计算 (1-t)t 以减少乘法运算。

6. 与其他函数组合

6.1 结合 clamp 限制插值范围

float t = someValue;
t = clamp(t, 0.0, 1.0);  // 确保 t 在 [0,1] 内
fixed4 col = lerp(_ColorA, _ColorB, t);

6.2 多阶段插值

// 三阶段插值:红→绿→蓝
float t = frac(_Time.y);  // 0~1 循环
fixed4 col;
if (t < 0.5) {
    col = lerp(_Red, _Green, t * 2);  // 0~0.5: 红→绿
} else {
    col = lerp(_Green, _Blue, (t - 0.5) * 2);  // 0.5~1: 绿→蓝
}

总结

lerp 是 Shader 中最基础的数学函数之一,通过组合不同的插值因子(如时间、距离、法线),你可以实现:

  • 颜色渐变与混合
  • 基于条件的效果切换
  • 动画过渡
  • 透明度与遮罩
  • 程序化纹理生成

在 Shader 编程中,除了基础的 lerp(线性插值)外,还有许多其他插值函数用于实现更复杂、更自然的过渡效果。以下是一些常用的插值函数及其应用场景:

1. smoothstep(平滑插值)

实现 S 形曲线插值,在两端变化缓慢,中间变化快速:

float smoothstep(float edge0, float edge1, float x);
  • 原理:当 x ≤ edge0 返回 0,当 x ≥ edge1 返回 1,中间使用 3t²-2t³ 多项式(其中 t 是归一化的 x)。
  • 应用场景:边缘淡化、光晕效果、自然过渡。

示例

float t = smoothstep(0.2, 0.8, i.uv.x);  // 水平方向平滑过渡
fixed4 col = lerp(_ColorA, _ColorB, t);  // 应用平滑插值

2. step(阶跃函数)

简单的二元阶跃函数,用于创建硬边界:

float step(float edge, float x);
  • 原理:当 x < edge 返回 0,否则返回 1。
  • 应用场景:阈值判断、棋盘格图案。

示例

float grid = step(0.5, fract(i.uv.x * 10)) * step(0.5, fract(i.uv.y * 10));
fixed4 col = lerp(_ColorA, _ColorB, grid);  // 创建棋盘格

3. pow(幂函数插值)

使用幂函数调整插值曲线的形状:

float pow(float x, float y);
  • 应用场景:创建加速或减速效果(如 pow(t, 2) 加速,pow(t, 0.5) 减速)。

示例

float t = _Time.y * 0.5;  // 时间因子
float easedT = pow(t, 2);  // 加速插值
fixed4 col = lerp(_ColorA, _ColorB, easedT);

4. saturate(范围限制)

将值限制在 [0, 1] 范围内,常用于避免插值溢出:

float saturate(float x);
  • 等价于clamp(x, 0.0, 1.0)

示例

float t = sin(_Time.y) * 2.0;  // 范围 [-2, 2]
t = saturate(t);  // 限制在 [0, 1]
fixed4 col = lerp(_ColorA, _ColorB, t);

5. smoothstep 的变体

  • smootherstep:比 smoothstep 更平滑的过渡,使用 6t⁵-15t⁴+10t³ 多项式。
  • 自定义平滑函数
    float customSmooth(float t) {
        return t * t * (3.0 - 2.0 * t);  // 类似 smoothstep 的自定义函数
    }
    

6. 球形插值(Slerp)

用于向量的球面插值,保持向量长度不变:

float3 slerp(float3 a, float3 b, float t);
  • 应用场景:旋转动画、方向过渡。

示例

float3 fromDir = float3(0, 0, 1);
float3 toDir = float3(1, 0, 0);
float3 finalDir = slerp(fromDir, toDir, _Time.y);

7. 贝塞尔曲线插值

实现非线性路径插值:

float bezier(float a, float b, float c, float d, float t) {
    float u = 1 - t;
    float tt = t * t;
    float uu = u * u;
    float uuu = uu * u;
    float ttt = tt * t;
    
    return uuu * a + 3 * uu * t * b + 3 * u * tt * c + ttt * d;
}
  • 应用场景:自定义动画曲线、复杂运动路径。

8. 噪声函数(如 Perlin、Simplex)

用于自然随机插值:

// Unity 内置噪声函数
float noise(float2 p);
  • 应用场景:云朵、火焰、地形等自然效果。

示例

float n = noise(i.uv * 10 + _Time.y);
fixed4 col = lerp(_ColorA, _ColorB, n);  // 噪声驱动的颜色变化

9. 三次样条插值

实现更平滑的多控制点插值:

float cubicSpline(float p0, float p1, float p2, float p3, float t) {
    float a = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3;
    float b = p0 - 2.5 * p1 + 2 * p2 - 0.5 * p3;
    float c = -0.5 * p0 + 0.5 * p2;
    float d = p1;
    return a * t * t * t + b * t * t + c * t + d;
}
  • 应用场景:多关键帧动画、平滑过渡。

10. 自定义缓动函数

通过数学函数创建特殊插值曲线:

// 弹性缓动
float elasticOut(float t) {
    return sin(13 * 3.14159 * t) * pow(2, -10 * t);
}

总结

函数名 特点 应用场景
smoothstep S 形平滑过渡 光晕、边缘淡化
step 硬边界阶跃 阈值判断、棋盘格
pow 指数调整曲线 加速/减速效果
saturate 范围限制 防止插值溢出
slerp 球面插值 向量方向过渡
贝塞尔/样条 多控制点插值 复杂动画路径
噪声函数 随机自然插值 自然效果(云、火)