应优先使用std::lerp(C++20,<cmath>)或glm::lerp(图形项目),避免手写(1-t)a+tb;必须用a+t*(b-a)形式防浮点误差,注意类型一致、t不自动clamp、四元数需slerp或nlerp。

直接用 std::lerp(C++20 起)或 glm::lerp(图形项目常用),比手写更安全;自己实现时必须用 a + t * (b - a) 形式,而非 (1-t)*a + t*b,否则浮点精度和边界行为容易出错。
std::lerp 是最简稳解,但要注意 C++ 标准版本
std::lerp 定义在 <cmath> 中,是 C++20 引入的标准化实现,内部已处理浮点稳定性、NaN 传播和极端值(如 inf)行为。
- 它强制使用
a + t * (b - a)算法,避免(1-t)*a在t ≈ 1时因舍入导致非零残差 - 参数类型必须一致:
std::lerp(1.0, 2.0, 0.5)合法,std::lerp(1, 2.0, 0.5)会编译失败(模板推导不一致) - 不自动 clamp
t:传入t = 2.0会外插,结果为a + 2*(b-a),符合数学定义,但需业务侧确认是否允许 - 若项目未启用 C++20,别 fallback 到宏或手写
(1-t)*a+t*b——优先引入 GLM 或自己写带 double 中间计算的模板
glm::lerp 支持向量和标量,但 t 类型易踩坑
GLM 的 glm::lerp 是图形开发事实标准,支持 float、vec3、quat 等,但参数匹配规则严格。
-
t可以是标量(全分量同权重)或同维向量(各分量独立插值),例如:glm::lerp(v1, v2, glm::vec3(0.1f, 0.5f, 0.9f)) - 常见错误:
glm::lerp(vec3(0), vec3(1), 1/2)返回全 0 向量——因为1/2是整数除法,结果为0,不是0.5f - 四元数插值慎用:
glm::lerp(q0, q1, t)是纯线性混合,结果不保证单位长度;动画中应改用glm::slerp或先glm::normalize(glm::lerp(...)) - 头文件只需
#include <glm/gtc/constants.hpp>和#include <glm/gtx/compatibility.hpp>(视 GLM 版本而定),无需全量包含
自己写泛型 lerp 模板,关键在类型转换和 t 归一化
当不能依赖 C++20 或 GLM 时,手写模板必须解决三类问题:整数除法陷阱、浮点精度损失、自定义类型兼容性。
立即学习“C++免费学习笔记(深入)”;
- 永远把输入转
double计算再转回,例如:static_cast<T>(static_cast<double>(a) + t * (static_cast<double>(b) - static_cast<double>(a))) - 不要用
auto推导中间类型:若T=int、t=float,a + t*(b-a)可能被推成int导致截断 - 对
t做显式浮点转换:从帧索引算t时写static_cast<double>(i) / (n-1),而非i/(n-1) - 若需 clamp,用
std::clamp(t, 0.0, 1.0)(C++17 起),别手写 if —— 边界值t=0.0和t=1.0必须精确命中,否则插值端点可能漂移
四元数插值不能直接套用标量 lerp 公式
对 quat 类型调用普通 lerp,得到的是弦上点,不是球面上的等角速路径;结果四元数模长 ≠ 1,旋转会缩放或失真。
- 最简补救:用
Nlerp—— 先线性混合,再归一化:normalize(lerp(q0, q1, t)) - 真正均匀角速度:必须用
Slerp,涉及反余弦和三角函数,且需处理点积接近 ±1 的退化情况(如用dot(q0,q1) 则翻转 <code>q1) - GLM 中
glm::slerp已内置这些保护,但性能比lerp低约 3–5 倍;高频更新(如每帧相机朝向)建议预计算或用Nlerp+ 小角度 fallback - 切记:欧拉角不能直接 lerp,角度回绕(如 170° → −170°)会导致反转;必须先转四元数再插值
真正麻烦的从来不是公式本身,而是 t 从哪来、有没有被隐式整数截断、以及插值对象是否真的生活在“直线空间”里——坐标是,四元数不是,颜色 RGB 通常也不是(应转 Lab 或 HSV 插值)。


















