在学习 Bézier 曲线时,除了知道如何计算曲线上的点 B(t),还需要理解曲线的导数 B'(t)。
如果说:
B(t)
表示曲线在参数 t 处的点,那么:
B'(t)
表示曲线在参数 t 处的切向量。
也就是说:
B(t) -> 曲线上的点
B'(t) -> 曲线在该点处的方向
导数在曲线绘制、路径动画、曲线连接、平滑性判断、法线计算和曲率计算中都非常重要。
普通函数的导数表示变化率。
对于参数曲线:
B(t) = (x(t), y(t))
它的导数是:
B'(t) = (x'(t), y'(t))
这表示:当参数 t 稍微增加时,曲线点会往哪个方向移动,以及移动得有多快。
所以,Bézier 曲线的导数可以理解为:
曲线在某个参数位置的运动方向
如果只关心方向,可以把 B'(t) 归一化,得到单位切向量。
一次 Bézier 曲线有两个控制点:
P0, P1
公式是:
B(t) = (1 - t)P0 + tP1
展开:
B(t) = P0 + t(P1 - P0)
对 t 求导:
B'(t) = P1 - P0
这说明一次 Bézier 曲线,也就是线段,它在任意位置的方向都相同。
它的导数是一个常量向量:
P0 -> P1
二次 Bézier 曲线有三个控制点:
P0, P1, P2
公式是:
B(t) = (1 - t)^2 P0 + 2(1 - t)t P1 + t^2 P2
它的一阶导数是:
B'(t) = 2(1 - t)(P1 - P0) + 2t(P2 - P1)
这个公式可以看成一条一次 Bézier 曲线。
令:
D0 = 2(P1 - P0)
D1 = 2(P2 - P1)
那么:
B'(t) = (1 - t)D0 + tD1
也就是说:
二次 Bézier 曲线的导数是一条一次 Bézier 曲线。
这里的 D0 和 D1 不是普通点,而是由原控制点相邻差值构成的向量。
三次 Bézier 曲线有四个控制点:
P0, P1, P2, P3
三次 Bézier 曲线公式是:
B(t) =
(1 - t)^3 P0
+ 3(1 - t)^2t P1
+ 3(1 - t)t^2 P2
+ t^3 P3
它的一阶导数是:
B'(t) =
3(1 - t)^2(P1 - P0)
+ 6(1 - t)t(P2 - P1)
+ 3t^2(P3 - P2)
这个公式也可以看成一条二次 Bézier 曲线。
令:
D0 = 3(P1 - P0)
D1 = 3(P2 - P1)
D2 = 3(P3 - P2)
则:
B'(t) =
(1 - t)^2D0
+ 2(1 - t)tD1
+ t^2D2
所以:
三次 Bézier 曲线的导数是一条二次 Bézier 曲线。
假设原 Bézier 曲线是 n 次曲线,控制点为:
P0, P1, P2, ..., Pn
那么它的一阶导数仍然是一条 Bézier 曲线,但次数降低一阶。
也就是说:
n 次 Bézier 曲线的一阶导数 = n-1 次 Bézier 曲线
导数曲线的新控制点由原控制点的相邻差值构成:
D0 = n(P1 - P0)
D1 = n(P2 - P1)
D2 = n(P3 - P2)
...
D(n-1) = n(Pn - P(n-1))
通用公式是:
Di = n(P(i+1) - Pi)
其中:
i = 0, 1, ..., n - 1
因此:
原曲线:n 次,有 n + 1 个控制点
导数曲线:n - 1 次,有 n 个控制点
这是 Bézier 曲线导数最重要的结论。
Bézier 曲线的导数表示曲线在某个位置的切向量。
对于三次 Bézier 曲线:
P0, P1, P2, P3
有:
B'(0) = 3(P1 - P0)
B'(1) = 3(P3 - P2)
这说明:
曲线起点处的切线方向由 P0 -> P1 决定
曲线终点处的切线方向由 P2 -> P3 决定
所以在三次 Bézier 曲线中:
P1 控制起点附近的方向
P2 控制终点附近的方向
更准确地说,控制的是起点和终点附近的切向量方向。
在参数 t 处,B'(t) 就是曲线的切向量。
如果只需要方向,可以计算:
T(t) = normalize(B'(t))
其中 T(t) 是单位切向量。
这在路径动画中很常见。例如一个物体沿曲线运动时,可以用切向量决定物体朝向。
如果两条 Bézier 曲线连接在一起:
第一条曲线的终点 = 第二条曲线的起点
那么它们至少是位置连续的。
如果连接处的切线方向也一致,那么视觉上会更平滑。
对于两条三次 Bézier 曲线:
第一条:
P0, P1, P2, P3
第二条:
Q0, Q1, Q2, Q3
如果:
P3 = Q0
表示位置连续。
如果:
P3 - P2
和:
Q1 - Q0
方向一致,则连接处切线方向一致。
如果导数向量完全相等:
3(P3 - P2) = 3(Q1 - Q0)
也就是:
P3 - P2 = Q1 - Q0
则连接处一阶导数连续。
这类连续性在曲线拼接、字体轮廓、CAD 建模中都很重要。
如果想知道曲线在 x 或 y 方向上的最大值或最小值,需要分析导数。
对于二维曲线:
B(t) = (x(t), y(t))
如果想找 x 方向的极值,需要解:
x'(t) = 0
如果想找 y 方向的极值,需要解:
y'(t) = 0
例如曲线的最高点、最低点、最左点、最右点,都和导数有关。
有了切向量,就可以进一步计算法向量。
在二维中,如果切向量是:
T = (x, y)
那么一个法向量可以写成:
N = (-y, x)
法向量可以用于曲线偏移、描边、轮廓生成等操作。
曲率则用于描述曲线在某一点弯曲得有多厉害。曲率需要一阶导数和二阶导数共同计算。
简单理解:
一阶导数 B'(t):表示曲线方向
二阶导数 B''(t):表示方向变化趋势
曲率:表示曲线弯曲程度
学习阶段可以先用同一个 Vec3 类型表示点和向量:
struct Vec3 {
double x, y, z;
Vec3 operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
Vec3 operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
Vec3 operator*(double s) const {
return {x * s, y * s, z * s};
}
};
线性插值:
Vec3 lerp(const Vec3& a, const Vec3& b, double t) {
return a * (1.0 - t) + b * t;
}
通用 De Casteljau 求值:
Vec3 deCasteljau(std::vector<Vec3> points, double t) {
while (points.size() > 1) {
std::vector<Vec3> next;
next.reserve(points.size() - 1);
for (size_t i = 0; i + 1 < points.size(); ++i) {
next.push_back(lerp(points[i], points[i + 1], t));
}
points = std::move(next);
}
return points.front();
}
通用 Bézier 导数求值:
Vec3 bezierDerivative(
const std::vector<Vec3>& controlPoints,
double t
) {
int n = static_cast<int>(controlPoints.size()) - 1;
if (n <= 0) {
return {0.0, 0.0, 0.0};
}
std::vector<Vec3> derivativePoints;
derivativePoints.reserve(n);
for (int i = 0; i < n; ++i) {
derivativePoints.push_back(
(controlPoints[i + 1] - controlPoints[i]) * n
);
}
return deCasteljau(derivativePoints, t);
}
使用示例:
std::vector<Vec3> points = {
{0, 0, 0},
{3, 8, 0},
{7, -8, 0},
{10, 0, 0}
};
double t = 0.5;
Vec3 p = deCasteljau(points, t);
Vec3 tangent = bezierDerivative(points, t);
其中:
p 是曲线在 t 处的点 B(t)
tangent 是曲线在 t 处的切向量 B'(t)
如果只需要方向,可以归一化:
double length(const Vec3& v) {
return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
Vec3 normalize(const Vec3& v) {
double len = length(v);
if (len == 0.0) {
return {0.0, 0.0, 0.0};
}
return {v.x / len, v.y / len, v.z / len};
}
使用:
Vec3 unitTangent = normalize(tangent);
Bézier 曲线导数的核心规则是:
Di = n(P(i+1) - Pi)
也就是说:
导数曲线的控制点 = 原曲线相邻控制点差值 × 原曲线次数
因此:
n 次 Bézier 曲线的一阶导数是 n-1 次 Bézier 曲线
导数的几何意义是:
B'(t) 表示曲线在参数 t 处的切向量
它可以用于:
1. 求曲线方向
2. 判断曲线连接是否平滑
3. 求极值点
4. 计算法线
5. 计算曲率
6. 做路径动画和几何建模分析
一句话记住:
Bézier 曲线的导数,就是由相邻控制点差向量组成的一条低一阶 Bézier 曲线。