贝塞尔曲线(Bézier curve)是计算机图像中非常重要的参数曲线,在平面设计、工程绘图、动画设计等计算机绘图领域中有着十分广泛的应用。(本文中的曲线特指 “贝塞尔曲线” )
一条贝塞尔曲线通过一组 “控制点” 的位置来控制该曲线的形状,这些控制点为 “\(P_0\)、\(P_1\)、\(P_2\)、…… 、\(P_n\)”。若某条贝塞尔曲线有 n 个控制点,那么可以称其为 n-1 次贝塞尔曲线,反之,一条 n 次贝塞尔曲线则有 n+1 个控制点。例如:最常用的 3 次贝塞尔曲线共有 4 个控制点。在这些控制点的影响下,点 \(B_{(t)}\) 连续的运动轨迹便形成了贝塞尔曲线。
那么贝塞尔曲线具体是如何绘制的呢?
1 德卡斯特里奥算法
德卡斯特里奥算法(de Casteljau's algorithm)的核心是 “递归的线性插值(Linear interpolation)” ,它通过重复的线性插值操作而构造出光滑的曲线。德卡斯特里奥算法基于这样一个简单的几何操作:在两点间的线段上,按比例 t 取一个点。这种算法具有 “效率高、较稳定” 的特点,是计算机绘制贝塞尔曲线的常用算法。
1.1 一次贝塞尔曲线
如下图,设想在平面上有两个已知定点 \(P_0\)和\(P_1\),为表示线段 \(P_0P_1\),在该线段上引入一个动点 \(B_{(t)}\),使得线段满足比例 \(\frac{P_0B_{(t)}}{P_0P_1 } =t\)(其中 \(0\le t \le 1\)),这个点在线段上随着参数 t 的变化而移动,线段 \(P_0P_1\) 即为点 \(B_{(t)}\) 的运动轨迹,可表示为该点包含参数 t 的坐标。那么如何求出动点 \(B_{(t)}\) 的坐标?
图 1-1
1)坐标法
将上述线段放入平面直角坐标系中,令:
\[\left \{
\begin{aligned}
P_0&=\left ( x_0 , y_0 \right )\\
P_1&=\left ( x_1 , y_1 \right )\\
B_{(t)}&=\left ( x_t , y_t \right )
\end{aligned}
\right.
\]
易得:
\[\left \{ \begin{aligned} x_t&=x_0+t(x_1-x_0)\\ y_t&=y_0+t(y_1-y_0) \end{aligned} \right.
\]
即 \(B_{(t)}=P_0+t(P_1-P_0)\),整理可得:
$$B_{(t)}=(1-t)P_0+tP_1 \qquad t \in [0,1]$$
2)向量法
由于求 \(B(t)\) 的坐标,可转换为向量运算。由图 1-1 易得:
\[\begin{aligned} \overrightarrow{P_0B_{(t)}} &= t \overrightarrow{P_0P_1} \\ \overrightarrow{P_0P_1} &= \overrightarrow{P_1} - \overrightarrow{P_0}\\ \overrightarrow{P_0B_{(t)}} &= \overrightarrow{B_{(t)}} - \overrightarrow{P_0} \end{aligned}
\]
因此:
\[\begin{aligned} \overrightarrow{B_{(t)}} &= \overrightarrow{P_0} + \overrightarrow{P_0B_{(t)}} \\ &= \overrightarrow{P_0} + t\overrightarrow{P_0P_1} \\ &= \overrightarrow{P_0} + t(\overrightarrow{P_1} - \overrightarrow{P_0})\\ &= (1-t)\overrightarrow{P_0} + t\overrightarrow{P_1} \end{aligned}
\]
用粗体表示向量,则:
\[\mathbf{B}_{(t)} = (1-t)\mathbf{P}_0 + t\mathbf{P}_1 \qquad t \in [0,1]
\]
为避免混淆,分别用 \(M\) 与 \(N\) 代替 \(P_0\) 与 \(P_1\),那么上述式子可写作:
\[\boxed{
\mathbf{B}_{(t)} = (1-t)\mathbf{M} + t\mathbf{N} \qquad t \in [0,1]
}
\]
这便是通过 “线性插值” 得出的公式,它表明点 \(B_{(t)}\) 在线段 \(MN\) 上运动可以用上述公式来表示。也就是说,该公式可以精确描述某动点 \(B_{(t)}\) 在任意两点\(M\)、\(N\)间的运动轨迹(其中 \(0\le t \le 1\))——这个轨迹是连接 \(M\) 和 \(N\) 的直线,它构成了线段 \(MN\)。若 t 从0开始一直增加到1,那么当\(t=0\)时,动点\(B_{(t)}\)位于该线段的起点\(M\)处;当\(t=1\)时,动点\(B_{(t)}\)位于该线段的终点\(N\)处。
1.2 二次贝塞尔曲线
如下图,二次贝塞尔曲线具有三个控制点 \(P_0\)、\(P_1\) 和 \(P_2\),类似上文的一次贝塞尔曲线,在线段 \(P_0P_1\)、\(P_1P_2\) 上分别有一动点 \(B_{1(t)}\)、\(B_{2(t)}\),在这两个动点所构成的线段 \(B_{1(t)}B_{2(t)}\) 上又存在一动点 \(B^2_{(t)}\),这几个动点均随 t 的变化而移动(其中 \(0\le t \le 1\)),\(B^2_{(t)}\) 的运动轨迹便形成了一条二次贝塞尔曲线。那么如何求得动点 \(B^2_{(t)}\) 的坐标?
图 1-2
使用上文推导的公式 \(\mathbf{B}_{(t)} = (1-t)\mathbf{M} + t\mathbf{N}\),\(t \in [0,1]\) 进行计算:
对于 \(B_{1(t)}\)、\(B_{2(t)}\) 和 \(B^2_{(t)}\),有:
\[\left \{ \begin{aligned} \mathbf{B}_{1(t)} &= (1-t)\mathbf{P}_0 + t \mathbf{P}_1\\ \mathbf{B}_{2(t)} &= (1-t)\mathbf{P}_1 + t \mathbf{P}_2\\ \mathbf{B}^2_{(t)} &= (1-t)\mathbf{B}_{1(t)} + t \mathbf{B}_{2(t)} \end{aligned} \right.
\]
为求 \(B^2_{(t)}\),需进行一次递归,将上述 \(B_{1(t)}\) 和 \(B_{2(t)}\) 代入到 \(B^2_{(t)}\) 的式子中,得:
\[\mathbf{B}^2_{(t)} = (1-t)[(1-t)\mathbf{P}_0 + t \mathbf{P}_1] + t[(1-t)\mathbf{P}_1 + t \mathbf{P}_2]
\]
整理得:
\[\mathbf{B}^2_{(t)} = (1-t)^2\mathbf{P}_0 + 2t(1-t)\mathbf{P}_1 + t^2\mathbf{P}_2 \qquad t \in [0,1]
\]
这个 \(B^2_{(t)}\) 的表达式所描述的即为二次贝塞尔曲线(Quadratic Bézier curves)。在字体设计中,TrueType 类型的曲线就是二次贝塞尔曲线。
1.3 三次贝塞尔曲线
如下图,现给出四个控制点 \(P_0\)、\(P_1\)、\(P_2\) 和 \(P_3\),在线段 \(P_0P_1\)、\(P_1P_2\)、\(P_2P_3\) 上分别有一动点 \(B_{1(t)}\)、\(B_{2(t)}\)、\(B_{3(t)}\),在由这三个动点构成的线段 \(B_{1(t)}B_{2(t)}\)、\(B_{2(t)}B_{3(t)}\) 上分别存在动点 \(B^2_{1(t)}\)、\(B^2_{2(t)}\),而这两个动点构成的线段 \(B^2_{1(t)}B^2_{2(t)}\) 上还存在一动点 \(B^3_{(t)}\),上述动点均随 t 的变化而移动(其中 \(0\le t \le 1\)),\(B^3_{(t)}\) 的运动轨迹便形成了一条三次贝塞尔曲线。那么如何求得动点 \(B^3_{(t)}\) 的坐标?
图 1-3
根据上文所得结论,不难得到:
\[\left \{ \begin{aligned} \mathbf{B}_{1(t)} &= (1-t)\mathbf{P}_0 + t\mathbf{P}_1 \\ \mathbf{B}_{2(t)} &= (1-t)\mathbf{P}_1 + t\mathbf{P}_2 \\ \mathbf{B}_{3(t)} &= (1-t)\mathbf{P}_2 + t\mathbf{P}_3 \\ \mathbf{B}^2_{1(t)} &= (1-t)\mathbf{B}_{1(t)} + t\mathbf{B}_{2(t)} \\ \mathbf{B}^2_{2(t)} &= (1-t)\mathbf{B}_{2(t)} + t\mathbf{B}_{3(t)} \\ \mathbf{B}^3_{(t)} &= (1-t)\mathbf{B}^2_{1(t)} + t\mathbf{B}^2_{2(t)} \end{aligned} \right.
\]
为求得 \(B^3_{(t)}\),需要进行 2 次递归,可得:
\[\begin{aligned} \mathbf{B}^3_{(t)} &= (1-t)^2\mathbf{B}_{1(t)} + 2t(1-t)\mathbf{B}_{2(t)} + t^2\mathbf{B}_{3(t)} \\ &= (1-t)^3\mathbf{P}_0 + 3t(1-t)^2\mathbf{P}_1 + 3t^2(1-t)\mathbf{P}_2 + t^3\mathbf{P}_3 \end{aligned}
\]
即:
\[\mathbf{B}^3_{(t)} = (1-t)^3\mathbf{P}_0 + 3t(1-t)^2\mathbf{P}_1 + 3t^2(1-t)\mathbf{P}_2 + t^3\mathbf{P}_3 \qquad t \in [0,1]
\]
这个 \(B^3_{(t)}\) 的表达式所描述的即为三次贝塞尔曲线(Cubic Bézier curves)。三次贝塞尔曲线在计算机绘图中的运用非常广泛,如大多数平面设计中矢量图的绘制、工程绘图等,在字体设计中,PostScript 类型的曲线使用的就是三次贝塞尔曲线。
2 高阶贝塞尔曲线与伯恩斯坦多项式
通过前文对 1 至 3 次贝塞尔曲线的推导,不难发现,在经过多次递归后,各个控制点 \(P_{i}\) 的系数可以认为是一个动态的权重,这个权重系数可以写成多项式的形式:
\[\binom{n}{i}t^i(1-t)^{n-i} \qquad i = 0, 1, 2,...,n
\]
因此,一般地,n 阶贝塞尔曲线的表达式可以写作:
\[\mathbf{B}_{(t)} = \sum\limits_{i=0}^{n} \binom{n}{i} \mathbf{P}_i(1-t)^{n-i}t^i \qquad \left \{ \begin{aligned} i &= 0, 1, 2,...,n\\ t &\in [0,1] \end{aligned} \right.
\]
进一步整理可得:
\[\color{FireBrick}
\boxed{
\mathbf{B}^n_{(t)} = \sum\limits_{i=0}^{n}\mathbf{P}_i\mathbf{b}_{i,n}(t) \qquad
t \in [0,1]
}
\]
这个式子就是 n 次贝塞尔曲线的伯恩斯坦形式,其中 \(\mathbf{b}_{i,n}(t)\) 被称为 n 阶的伯恩斯坦多项式(Bernstein polynomial),也可以称作伯恩斯坦基函数。即:
\[\color{FireBrick}
\boxed{
\mathbf{b}_{i,n}(t) = \binom{n}{i}t^i(1-t)^{n-i} \qquad
i = 0,1,2,...,n
}
\]
在上述伯恩斯坦形式的 n 阶贝塞尔曲线公式中,\(\mathbf{B}^n_{(t)}\) 中的 n 表示贝塞尔曲线的次数,\(\mathbf{P}_i\) 表示贝塞尔曲线的控制点。
3 贝塞尔曲线的应用
3.1 贝塞尔曲线的拼接
在阶数过高的贝塞尔曲线中,单个控制点对整条曲线的影响是比较小的,所以高阶贝塞尔曲线调整起来比较麻烦。为了保证每段曲线不会过于复杂,减少计算的成本,在实际使用贝塞尔曲线进行计算机绘图时,常常使用分段的贝塞尔曲线——通过多条贝塞尔曲线拼接而成的曲线,使其能够形成更加复杂的形状。
为了使两个分段贝塞尔曲线能平滑连接,需要使连接点两侧的控制点与连接点共线,并且连接点与其两侧控制点所形成的两条线段长度应当相等。为什么?可以对 \(\mathbf{B}^n_{(t)}\) 的表达式进行研究。
图 3-1
如上图所示,设想有两段贝塞尔曲线 \(a\) 和 \(b\) 在点 \(P_x\) 处相连,那么不难发现在连接点 \(P_x\) 处有:
\[\mathbf{P}^{(a)}_n = \mathbf{P}_x = \mathbf{P}^{(b)}_0
\]
其中 \(\mathbf{P}^{(a)}_n\) 表示 a 段贝塞尔曲线的末端点坐标,\(\mathbf{P}^{(b)}_0\) 表示 b 段贝塞尔曲线起始点坐标。为了使曲线在连接点处平滑,应该使曲线函数 \(\mathbf{B}^n_{(t)}\) 的一阶导数在点 \(\mathbf{P}_x\) 处连续,即该点左右导数的极限相同。
将函数 \(\mathbf{B}^n_{(t)}\) 展开,得:
\[\begin{aligned}
\mathbf{B}^n_{(t)} =& \binom{n}{0}\mathbf{P}_0t^0(1-t)^n+\binom{n}{1}\mathbf{P}_1t^1(1-t)^{n-1}+\binom{n}{2}\mathbf{P}_2t^2(1-t)^{n-2}+...\\
+& \binom{n}{n-2}\mathbf{P}_{n-2}t^{n-2}(1-t)^2 + \binom{n}{n-1}\mathbf{P}_{n-1}t^{n-1}(1-t)^1 + \binom{n}{n}\mathbf{P}_nt^n(1-t)^0
\end{aligned}
\]
对伯恩斯坦基函数求一阶导数得:
\[\frac{d}{dt}\mathbf{b}_{i,n}(t) = n[\mathbf{b}_{i-1,n-1}(t)-\mathbf{b}_{i,n-1}(t)]
\]
那么函数 \(\mathbf{B}^n_{(t)}\) 的一阶导数即为:
\[\begin{aligned}
\frac{d}{dt}\mathbf{B}^n_{(t)} &= \sum\limits_{i=0}^{n}\mathbf{P}_i[\mathbf{b}_{i,n}(t)]^{\prime} \\
&= n\sum\limits_{i=0}^{n}[\mathbf{b}_{i-1,n-1}(t)-\mathbf{b}_{i,n-1}(t)]\mathbf{P}_i
\end{aligned}
\]
其中,约定当 \(i<0\) 或 \(i>n-1\) 时,\(\mathbf{b}_{i,n-1}(t) = 0\)。将上式展开,即:
\[\begin{aligned}
\frac{d}{dt}\mathbf{B}^n_{(t)} =& -\binom{n}{0}n\mathbf{P}_0(1-t)^{n-1}+\binom{n}{1}\mathbf{P}_1[(1-t)^{n-1}-t(n-1)(1-t)^{n-2}] \\
+& \binom{n}{2}\mathbf{P}_2[2(1-t)^{n-2}t-t^2(n-2)(1-t)^{n-3}] + ...\\
+& \binom{n}{n-2}\mathbf{P}_{n-2}[(n-2)(1-t)^2t^{n-3}-2(1-t)t^{n-2}] \\
+& \binom{n}{n-1}\mathbf{P}_{n-1}[(n-1)(1-t)^2t^{n-2}-t^{n-1}] +\binom{n}{n}n\mathbf{P}_nt^{n-1}
\end{aligned}
\]
对于 a 曲线点 \(P^{(a)}_n\),\(t=1\);对于 b 曲线点 \(P^{(b)}_0\),\(t=0\)。分别将 t 值代入上述导函数中,可得:
\[\left \{
\begin{aligned}
\frac{d\mathbf{B}^{(a)}_{(1)}}{dt} &= -\binom{n}{n-1}\mathbf{P}^{(a)}_{n-1} + \binom{n}{n}n\mathbf{P}^{(a)}_n\\
\frac{d\mathbf{B}^{(b)}_{(0)}}{dt} &= -\binom{n}{0}n\mathbf{P}^{(b)}_0 + \binom{n}{1}\mathbf{P}^{(b)}_1
\end{aligned}
\right.
\]
由于两条贝塞尔曲线在点 \(P_x\) 处相接,则:
\[\left \{
\begin{aligned}
\frac{d\mathbf{B}^{(a)}_{(1)}}{dt} &= \frac{d\mathbf{B}^{(b)}_{(0)}}{dt}\\
\mathbf{P}^{(a)}_n &= \mathbf{P}_x = \mathbf{P}^{(b)}_0
\end{aligned}
\right.
\]
得:
\[\begin{aligned}
-\binom{n}{n-1}\mathbf{P}^{(a)}_{n-1} + \binom{n}{n}n\mathbf{P}_x &=
- \binom{n}{0}n\mathbf{P}_x + \binom{n}{1}\mathbf{P}^{(b)}_1 \\
-\frac{n!}{(n-1)!}\mathbf{P}^{(a)}_{n-1} + n\mathbf{P}_x
&= -n\mathbf{P}_x + \frac{n!}{(n-1)!}\mathbf{P}^{(b)}_1 \\
-n\mathbf{P}^{(a)}_{n-1} + n\mathbf{P}_x &= -n\mathbf{P}_x + n\mathbf{P}^{(b)}_1 \\
-\mathbf{P}^{(a)}_{n-1} + \mathbf{P}_x &= -\mathbf{P}_x + \mathbf{P}^{(b)}_1
\end{aligned}
\]
即:
\[\color{FireBrick}
\mathbf{P}^{(a)}_n = \mathbf{P}^{(b)}_0 = \mathbf{P}_x = \frac{\mathbf{P}^{(a)}_{n-1} + \mathbf{P}^{(b)}_1}{2}
\]
从上述等式中可以发现,连接点 \(P_x\) 的坐标仅仅和这两条贝塞尔曲线倒数第二和第二个控制点坐标有关,而且是两者之和的一半!这表明若要使两个分段贝塞尔曲线能平滑相连,那么连接点必须落在 \(P^{(a)}_{n-1}\) 和 \(P^{(b)}_1\) 两点构成的线段上,而且是这条线段的中点。
3.2 一些贝塞尔曲线的特性
贝塞尔曲线存在以下的一些特性:
1、控制点: 从贝塞尔曲线的公式中可以发现,一条贝塞尔曲线的形状受到一组控制点的控制:\(\left \{ P_0, P_1, P_2, \dots , P_n \right \}\) ,这些控制点决定了曲线的起点、终点和弯曲形态。
2、端点: 一条贝塞尔曲线有 2 个控制点 \(P_0\)、\(P_n\) 分别位于曲线线端,这两个点决定了曲线的起始与结束。从图像上来看,这 2 个位于线端的控制点在曲线上,而其余控制点一般不在曲线上。观察 \(\mathbf{B}^n_{(t)}\) 的表达式,不难发现:当 \(t=0\) 时,对应曲线的起点,只有起始点 \(P_0\) 的系数不为 0;当 \(t = 1\) 时,对应曲线的终点,只有终止点 \(P_n\) 的系数不为 0。
3、端点切线: 贝塞尔曲线在起点 \(P_0\) 处的切线方向与 \(\left ( \overrightarrow{P_1}-\overrightarrow{P_0} \right )\) 平行,终点 \(P_n\) 处的切线方向与 \(\left ( \overrightarrow{P_n}-\overrightarrow{P_{n-1}} \right )\) 平行。
图 3-2
如上图,根据 3.1 中对 \(\mathbf{B}^n_{(t)}\) 进行的一阶求导,易得曲线起始点 \(P_0\) 和终止点 \(P_n\)(即 \(t = 0\) 和 \(t = 1\) 时)的一阶导数分别为:
\[\begin{aligned}
\frac{d}{dt}\mathbf{B}^n_{(0)} &= -\binom{n}{0}n\mathbf{P}_0(1-0)^{n-1}+\binom{n}{1}\mathbf{P}_1[(1-0)^{n-1}-0(n-1)(1-0)^{n-2}] \\
&= -n\mathbf{P}_0+n\mathbf{P}_1\\
\\
\frac{d}{dt}\mathbf{B}^n_{(1)} &= \binom{n}{n-1}\mathbf{P}_{n-1}[(n-1)(1-1)^21^{n-2}-1^{n-1}] +\binom{n}{n}n\mathbf{P}_n1^{n-1} \\
&= -n\mathbf{P}_{n-1} + n\mathbf{P}_n
\end{aligned}
\]
即:
\[\color{FireBrick}
\left \{
\begin{aligned}
\frac{d}{dt}\mathbf{B}^n_{(0)} &= n(\mathbf{P}_1 - \mathbf{P}_0) \\
\frac{d}{dt}\mathbf{B}^n_{(1)} &= n(\mathbf{P}_n - \mathbf{P}_{n-1})
\end{aligned}
\right.
\]
这表明贝塞尔曲线的起始点 \(P_0\) 的切线与前两个控制点 \(P_0\)、\(P_1\) 的连线平行,终止点 \(P_n\) 的切线与最后两个控制点 \(P_{n-1}\)、\(P_n\) 的连线平行。
4、凸多边形: 贝塞尔曲线会被包含在一个由所有控制点构成的最小凸多边形内。
5、系数对称: 控制点的系数具有对称性。对控制点 \(P_i\) 的系数伯恩斯坦多项式 \(\mathbf{b}_{i,n}(t)\) 进行考查:
当参数 \(t=t\),第 \(i\) 项系数(控制点 \(P_i\))为:
\[\mathbf{b}_{i,n}(t) = \binom{n}{i}t^i(1-t)^{n-i}
\]
当参数 \(t=1-t\),第 \(i = n - i\) 项系数(控制点 \(P_{n-i}\))为
\[\begin{aligned}
\mathbf{b}_{n-i,n}(1-t) &= \binom{n}{n-i}(1-t)^{n-i}(1-(1-t))^{n-(n-i)} \\
&= \binom{n}{n-1}(1-t)^{n-i}t^i
\end{aligned}
\]
由组合数的对称性得:
\[\binom{n}{i} = \binom{n}{n-i}
\]
所以:
\[\color{FireBrick}
\mathbf{b}_{i,n}(t) = \mathbf{b}_{(n-i),n}(1-t)
\]
因此,在一条贝塞尔曲线中,控制点的系数是对称的。
6、几何不变: 从贝塞尔曲线的表达式可知,它的几何特征不随坐标的变化而变化,其曲线形状仅仅与各个控制点的位置有关,而与坐标系的选择无关。
贝塞尔曲线通过离散的控制点来绘制平滑连续的曲线,可以认为是某种离散与连续的辩证统一。从应用上来说,最常用的贝塞尔曲线是二次贝塞尔曲线和三次贝塞尔曲线,为了熟练地掌握贝塞尔曲线的绘制技巧,还需要在实践中多加感悟。

