0x09.Shader函数lerp
在 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]
)。- 返回值:
a
和b
之间的线性插值结果:a * (1-t) + b * t
。
2. 工作原理
- 当
t = 0
时,返回a
。 - 当
t = 1
时,返回b
。 - 当
t = 0.5
时,返回a
和b
的平均值。 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 |
球面插值 | 向量方向过渡 |
贝塞尔/样条 | 多控制点插值 | 复杂动画路径 |
噪声函数 | 随机自然插值 | 自然效果(云、火) |