CAGD 学习笔记 | 02 Bézier 曲线

Bézier 曲线

Bézier 曲线指的是一段由若干控制点给出的多项式曲线, 它是计算机图形学中的一种重要的参数曲线. 首先, 我们给出 Bézier 曲线的定义.

Bézier 曲线的定义

对于空间中的 n+1n+1 个控制点 {p0,p1,,pn}\{p_0, p_1, \dots, p_n\}, 我们有一段 Bézier 参数曲线

p(t)=k=0nBkn(t)pk, 0t1(1)p(t) = \sum_{k=0}^n B^{n}_{k}(t) p_k,\ 0 \leq t \leq 1 \tag{1}

其中

Bkn(t)=(nk)tk(1t)nk=n!(nk)!k!tk(1t)nk, 0knB_k^n(t) = \binom{n}{k} t^k (1-t)^{n-k} = \frac{n!}{(n-k)!k!} t^k (1-t)^{n-k},\ 0 \leq k \leq n

nn 阶的 Bernstein 基函数. 这条 Bézier 曲线是 nn 阶的多项式曲线.

在深入探讨 Bézier 曲线之前, 我们可以首先观察一下它的构造方式. Bézier 曲线的构造方式是用控制点对 Bernstein 基函数进行线性组合, 控制点相当于基函数的权重. 因此它是一条有显式表达式的多项式曲线, 又是光滑的 (任意阶可导), 而且容易在计算机中计算[1]. 这些性质使得 Bézier 曲线成为了在计算机图形学中重要的曲线.

Bernstein 基函数

要理解 Bézier 曲线的意义, 首先需要理解 Bernstein 基函数的意义, 而 Bernstein 基函数则是由 Bernstein 在逼近理论的证明中提出的一组多项式函数.

逼近理论

逼近理论指的是, 用简单的函数来逼近一个复杂的函数, 并且使得误差控制在可以接收的范围内. 这里所需要讨论的是, 如何用多项式函数逼近定义在闭区间上的连续函数. Weierstrass 在 1885 年证明了如下重要的定理:

Weierstrass Approximation Theoremff[0,1][0, 1] 上的连续函数, 则对于 ϵ>0\forall \epsilon > 0, 存在多项式 pp, 使得 maxt[0,1]f(t)p(t)<ϵ\max_{t \in [0, 1]} |f(t) - p(t)| < \epsilon.

即多项式能一致逼近闭区间上的连续函数. 而 Bernstein 在 1912 年给出了上述定理的一个构造性的证明: ffnn 阶 Bernstein 多项式列

pn(t)=k=0nf(kn)Bkn(t)p_n(t) = \sum_{k=0}^n f\left(\frac{k}{n}\right) B_k^n(t)

是一致逼近 ff 的一个函数列.

根据 Bernstein 的证明, 我们知道如果这些点是采样于一段连续的曲线, 那么随着 nn 增大, Bézier 曲线将能任意逼近这条曲线. 也就是说, Bézier 曲线的一大意义在于它能在给定的误差范围内表示任何一条连续曲线.

性质

Bernstein 基函数有如下性质:

  • k=0nBkn(t)=k=0n(nk)tk(1t)nk=(t+(1t))n=1\sum_{k=0}^n B_k^n(t) = \sum_{k=0}^n \binom{n}{k} t^k (1-t)^{n-k} = (t + (1-t))^n = 1 就是二项式展开式;
  • t[0,1],0Bkn(t)1\forall t \in [0, 1], 0 \leq B^n_k(t) \leq 1;
  • Bkn(t)=Bnkn(1t)B^n_k(t) = B^n_{n-k}(1-t);
  • Bkn(0)=δk,0, Bkn(1)=δk,nB^n_k(0) = \delta_{k,0},\ B^n_k(1) = \delta_{k, n};
  • Bernstein 基函数的导数的性质将在下一节中介绍.

导数

本节中主要讨论 Bernstein 基函数和 Bézier 曲线的导数.

对 Bernstein 基函数求导得

ddtBkn(t)=(nk)(ktk1(1t)nk(nk)tk(1t)nk1)=(nk)(k(n1k1)1Bk1n1(t)(nk)(n1k)1Bkn1(t))=n(Bk1n1(t)Bkn1(t))(2)\begin{aligned} \frac{\mathrm{d}}{\mathrm{d} t} B_k^n(t) & = \binom{n}{k} \left( k \cdot t^{k-1} (1-t)^{n-k} - (n-k) t^k (1-t)^{n-k-1} \right) \\ & = \binom{n}{k} \left( k \binom{n-1}{k-1}^{-1} B_{k-1}^{n-1}(t) - (n-k) \binom{n-1}{k}^{-1} B_k^{n-1}(t) \right) \\ & = n (B^{n-1}_{k-1}(t) - B_{k}^{n-1}(t)) \end{aligned} \tag{2}

特别地, 为了保证形式上的一致性 (如上式中 k=0k = 0, 会出现 B1n1(t)B^{n-1}_{-1}(t)), 可以约定当 k<0k<0Bkn(t)=0B^{n}_{k}(t) = 0.
p
利用式 (2) 求二阶导得

d2dt2Bkn(t)=ddtn(Bk1n1(t)Bkn1(t))=n(n1)(Bk2n2(t)2Bk1n2(t)+Bkn2(t))\begin{aligned} \frac{\mathrm{d}^2}{\mathrm{d} t^2} B^{n}_{k}(t) & = \frac{\mathrm{d}}{\mathrm{d} t} n (B^{n-1}_{k-1}(t) - B^{n-1}_k(t)) \\ & = n(n-1)(B^{n-2}_{k-2}(t) - 2B^{n-2}_{k-1}(t)+B^{n-2}_{k}(t)) \end{aligned}

事实上, Bernstein 基函数的 rr 阶导数为

drdtrBkn(t)=(1)rn!(nr)!ΔrBkrnr(t)=n!(nr)!l=0r(rl)(1)lBkr+lnr(t)\begin{aligned} \frac{\mathrm{d}^r}{\mathrm{d} t ^r} B^n_{k}(t) & = (-1)^r \frac{n!}{(n-r)!} \Delta^r B^{n-r}_{k-r}(t) \\ & = \frac{n!}{(n-r)!} \sum_{l=0}^r \binom{r}{l} (-1)^l B^{n-r}_{k-r+l}(t) \end{aligned}

其中 Δ\Delta 为差分算子

Δak=ak+1akΔrak=Δr1ak+1Δr1ak=(1)rl=0r(rl)(1)lak+l\begin{aligned} \Delta a_k & = a_{k+1} - a_k \\ \Delta^r a_k & = \Delta^{r-1} a_{k+1} - \Delta^{r-1} a_k = (-1)^r \sum_{l=0}^r \binom{r}{l} (-1)^{l}a_{k+l} \end{aligned}

对 Bézier 曲线求导得

ddtp(t)=k=0nddtBkn(t)pk=nk=0n(Bk1n1(t)Bkn1(t))pk=nk=0n1Bkn1(t)(pk+1pk)\begin{aligned} \frac{\mathrm{d}}{\mathrm{d} t} p(t) & = \sum_{k=0}^n \frac{\mathrm{d}}{\mathrm{d} t}B^{n}_{k}(t) p_k \\ & = n \sum_{k=0}^n (B^{n-1}_{k-1}(t) - B^{n-1}_{k}(t)) p_k \\ & = n \sum_{k=0}^{n-1} B^{n-1}_{k}(t) (p_{k+1}-p_k) \end{aligned}

更高阶的导数为

drdtrp(t)=n!(nr)!k=0nrBknr(t)Δrpk\frac{\mathrm{d}^r}{\mathrm{d} t^r} p(t) = \frac{n!}{(n-r)!}\sum_{k=0}^{n-r}B^{n-r}_{k} (t) \Delta^r p_k

Bézier 曲线在两端的导数有较简单的形式如下

p(0)=n(p1p0)p(1)=n(pnpn1)p(0)=n(n1)(p22p1+p0)p(1)=n(n1)(pn2pn1+pn2)\begin{aligned} p'(0) & = n(p_1 - p_0) \\ p'(1) & = n(p_n - p_{n-1}) \\ p''(0) & = n(n-1)(p_2 - 2p_1 + p_0) \\ p''(1) & = n(n-1)(p_n - 2p_{n-1} + p_{n-2}) \end{aligned}

de Casteljau 算法

在实践中, 我们不使用 Bézier 曲线的显式表达式来绘制曲线, 因为这涉及到多项式和阶乘的计算, 这些计算在数值上是不稳定的, 并且计算量也较大. 我们通常使用 de Casteljau 算法来绘制 Bézier 曲线.

de Casteljau 算法是 Paul de Casteljau 于 1959 年开发的用于绘制 Bézier 曲线的算法. 事实上, Bézier 曲线就是这样被发明的.

三个控制点 p0,p1,p2p_0, p_1, p_2 的 Bézier 曲线使用 de Casteljau 算法的绘制过程如下

图 1 使用 de Casteljau 算法绘制 Bézier 曲线的过程

绘制是通过不断地迭代进行插值实现的, 记 pk(0)=pkp_k^{(0)} = p_k, 第一次迭代如下

p0(1)(t)=(1t)p0(0)+tp1(0)p1(1)(t)=(1t)p1(0)+tp2(0)\begin{aligned} p_{0}^{(1)} (t) & = (1-t) p_{0}^{(0)} + t p_{1}^{(0)} \\ p_{1}^{(1)} (t) & = (1-t) p_{1}^{(0)} + t p_{2}^{(0)} \\ \end{aligned}

用第一次迭代的结果进行第二次迭代

p0(2)(t)=(1t)p0(1)(t)+tp1(1)(t)p_{0}^{(2)} (t) = (1-t) p_{0}^{(1)}(t) + t p_{1}^{(1)} (t)

p0,2(t)p_{0, 2} (t) 就是所得的 Bézier 曲线. 注意到

p0(2)(t)=p0+2t(1t)p1+t2p2=k=02pkB2,k(t)p_{0}^{(2)} (t) = p_0 + 2 t (1-t) p_1 + t^2 p_2 = \sum_{k=0}^2 p_k B^{2, k} (t)

其中 Bk2(t)B_k^2(t) 是二阶的 Bernstein 基函数. 事实上, 由 de Casteljau 算法进行归纳可以得到所绘制的曲线就是式 (1) 所描述的曲线.

de Casteljau 算法的一大优点在于它的每一步都是计算两个点的凸组合, 这使得计算十分稳定, 而且每次迭代还可采取并行计算. 从 de Casteljau 算法还衍生出 Bézier 曲线的细分算法, 采用这一算法能更高效地绘制 Bézier 曲线.

细分

Bézier 曲线的细分 (Subdivision) 指的是, 在曲线上任取一点 p(s), 0<s<1p(s),\ 0<s<1, 可以将曲线在这一点处分为两段 Bézier 曲线, 即一段以 p0p_0 为起点并以 p(s)p(s) 为终点, 和一段以 p(s)p(s) 为起点并以 pnp_n 为终点的曲线. 显然, 细分之后得到的曲线仍然是 n 阶的 Bézier 曲线, 因此它仍然可以用 n+1n+1 个控制点来表示.

事实上, 这些控制点恰好是使用 de Casteljau 算法求点 p(s)p(s) 的位置过程中出现的点. 第一段曲线的控制点为 p0(0)(s),p0(1)(s),,p0(n)(s)p_0^{(0)}(s), p_0^{(1)}(s), \dots, p_0^{(n)}(s), 第二段曲线的控制点为 pn(0)(s),pn(1)(s),,pn(n)(s)p_n^{(0)}(s), p_n^{(1)}(s), \dots, p_n^{(n)}(s). 例如下图中所圈出的两组各三个点就是这一条 Bézier 曲线被细分成的两段曲线的控制点.

图 3 Bézier 曲线的细分

原理

下证这一性质. 设对 nn 阶 Bézier 曲线在 0<s<10<s<1 处进行细分, 要证明 p0(0)(s),p0(1)(s),,p0(n)(s)p_0^{(0)}(s), p_0^{(1)}(s), \dots, p_0^{(n)}(s) 所给出的 Bézier 曲线就是原本的曲线.

由 de Casteljau 算法, 知

p0(k)=l=0k(kl)sl(1s)klpl(0)(3)p_0^{(k)} = \sum_{l=0}^k \binom{k}{l} s^l (1-s)^{k-l} p_l^{(0)} \tag{3}

p0(0)(s),p0(1)(s),,p0(n)(s)p_0^{(0)}(s), p_0^{(1)}(s), \dots, p_0^{(n)}(s) 所给出的 Bézier 曲线为 p~(t)\tilde{p}(t), 参数域为 t[0,s]t \in [0, s], 则其显示表达式为

q(t)=k=0np0(k)(nk)tk(st)nksn(4)q(t) = \sum_{k=0}^n p_0^{(k)} \binom{n}{k} \frac{t^k (s-t)^{n-k}}{s^n} \tag{4}

将式 (3)(3) 代入式 (4)(4), 得

q(t)=k=0nl=0k(kl)sl(1s)klpl(0)(nk)tk(st)nksn=l=0nk=lnk!(kl)!n!(nk)!k!sln(1s)kltk(st)nkpl(0)=l=0n(nl)slntlk=ln(nlnk)((1s)t)kl(st)nkpl(0)=l=0n(nl)slntl((1s)t+(st))nlpl(0)=l=0n(nl)tl(1t)nlpl(0)=p(t)\begin{aligned} q(t) & = \sum_{k=0}^n \sum_{l=0}^k \binom{k}{l} s^l (1-s)^{k-l} p_l^{(0)} \binom{n}{k} \frac{t^k (s-t)^{n-k}}{s^n} \\ & = \sum_{l=0}^n \sum_{k=l}^n \frac{k!}{(k-l)!} \frac{n!}{(n-k)!k!} s^{l-n} (1-s)^{k-l} t^k (s-t)^{n-k} p_l^{(0)} \\ & = \sum_{l=0}^n \binom{n}{l} s^{l-n} t^l \sum_{k=l}^n \binom{n-l}{n-k} ((1-s) t)^{k-l} (s-t)^{n-k} p_l^{(0)} \\ & = \sum_{l=0}^n \binom{n}{l} s^{l-n} t^l ((1-s)t + (s-t))^{n-l} p_l^{(0)} \\ & = \sum_{l=0}^n \binom{n}{l} t^l (1-t)^{n-l} p_l^{(0)} \\ & = p(t) \end{aligned}

q(t)q(t)p(t)p(t) 是同一条曲线.

应用

对 Bézier 曲线进行细分得到的控制多边形会收敛到这一条 Bézier 曲线.[2]

用细分算法绘制 Bézier 曲线的算法如下:

  1. t=0.5t=0.5 处对曲线进行细分, 得到两段 Bézier 曲线;
  2. 若细分得到的这一段曲线足够接近一条直线段, 则绘制一条直线段;
  3. 否则递归地进行细分.

类似地, 可以用这样的方法求两条 Bézier 曲线的交点.

升阶

因为 nn 阶 Bernstein 基函数所张成的空间, 记作 B(n)\mathbf{B}^{(n)}, 就是 n 次多项式空间, 所以 B(n)\mathbf{B}^{(n)}B(n+1)\mathbf{B}^{(n+1)} 的子空间. 进一步地, 可以发现, nn 阶的 Bézier 曲线也是另一组控制点下的 n+1n+1 阶 Bézier 曲线.[3] 如下图所示, 这一段 4 个控制点的 3 阶 Bézier 曲线可以表示为 5 个控制点的 4 阶曲线. 求这样一组控制点的过程称为 Bézier 曲线的升阶 (Degree Elevation).

图 2 Bézier 曲线的升阶

原理

接下来介绍这另一组控制点的计算方法. 注意到

(1t)Bn,k(t)=n+1kn+1Bn+1,k(t)tBn,k(t)=k+1n+1Bn+1,k+1(t)\begin{aligned} (1-t)B^{n,k}(t) & = \frac{n+1-k}{n+1} B^{n+1, k}(t) \\ t \cdot B^{n,k}(t) & = \frac{k+1}{n+1}B^{n+1,k+1}(t) \end{aligned}

因此有

k=0nBkn(t)pk=k=0n((1t)+t)Bkn(t)pk=k=0n(n+1kn+1Bkn+1(t)+k+1n+1Bk+1n+1(t))pk=k=0nBkn+1(t)n+1kn+1pk+k=1n+1Bkn+1(t)kn+1pk1=B0n+1(t)p0+k=1nBkn+1(t)(n+1kn+1pk+kn+1pk1)+Bn+1n+1(t)pn\begin{aligned} \sum_{k=0}^n B^{n}_{k}(t) p_k & = \sum_{k=0}^n ((1-t) + t) B^{n}_{k}(t) p_k \\ & = \sum_{k=0}^n \left( \frac{n+1-k}{n+1} B^{n+1}_{k}(t) + \frac{k+1}{n+1}B^{n+1}_{k+1}(t) \right) p_k \\ & = \sum_{k=0}^n B^{n+1}_{k}(t) \frac{n+1-k}{n+1} p_k + \sum_{k=1}^{n+1} B^{n+1}_{k}(t) \frac{k}{n+1} p_{k-1} \\ & = B^{n+1}_{0}(t) p_0 + \sum_{k=1}^{n} B^{n+1}_{k}(t) \left( \frac{n+1-k}{n+1} p_k + \frac{k}{n+1} p_{k-1} \right) + B^{n+1}_{n+1}(t) p_n \end{aligned}

得到升阶后的 n+1n+1 阶 Bézier 曲线的控制点{pˉ0,pˉ1,,pˉn+1}\{\bar{p}_0, \bar{p}_1, \dots, \bar{p}_{n+1}\}

pˉ0=p0pˉk=kn+1pk1+(1kn+1)pk, 1knpˉn+1=pn\begin{aligned} \bar{p}_0 & = p_0 \\ \bar{p}_k & = \frac{k}{n+1} p_{k-1} + \left(1-\frac{k}{n+1}\right) p_k,\ 1 \leq k \leq n \\ \bar{p}_{n+1} & = p_n \\ \end{aligned}

应用

和升阶类似地, 对 Bézier 曲线进行不断升阶得到的控制多边形会收敛到这一条 Bézier 曲线.[2]

在实践中, 升阶可以使若干不同阶的 Bézier 曲线拥有相同的阶; 尽管更高阶的 Bézier 曲线需要更复杂的计算, 但是它也为曲线的编辑提供了更强的灵活性.

性质

Bézier 曲线有如下性质:

变差缩减性

变差缩减性 (Variation Diminishing Property) 指的是, 对于空间中的任意平面, 它与 Bézier 曲线的交点个数不会多于它与控制多边形的交点个数.

证明可以通过 Bézier 曲线的升阶得到. 如图 2, 升阶后的控制多边形中的任意一条不连接起点或终点的线段, 能与升阶前的控制多边形中的两条线段组成一个三角形. 考虑这一三角形, 若该平面与升阶得到的线段相交, 则升阶前的两段线段中必有一个与该平面相交. 因此升阶不会使得控制多边形与该平面的交点个数减少. 又因为不断升阶得到的控制多边形将收敛于这条 Bézier 曲线, 故曲线与这个平面的交点不会多于它与控制多边形的交点个数.

仿射无关性

仿射无关性 (Affine Invariance Property) 指的是, 对一条 Bézier 曲线作仿射变换, 等价于对它的控制点作仿射变换并在从这些点构造一条新的 Bézier 曲线.

这可以直接由 Bézier 曲线的定义容易地证明.

凸包性

凸包性 (Convex Hull Property) 指的是, Bézier 曲线上的点总位于控制点的凸包内.

证明是显然的.

其它性质

一条 Bézier 曲线是平面曲线等价于它的控制点在平面内

证明是显然的.

一条 Bézier 曲线的弧长不大于其控制多边形的周长.

证明如下: 由式 (2)(2)

01p(t)dt=n01i=0n1Bin1(t)(pi+1pi)dtn01i=0n1Bin1(t)(pi+1pi)dt=ni=0n101Bin1(t)dtpi+1pi=i=0n1pi+1pi\begin{aligned} \int_0^1 \left\|p^\prime(t)\right\| \mathrm{d}t & = n \int_0^1 \left\|\sum_{i=0}^{n-1}B_i^{n-1}(t)(p_{i+1}-p_i)\right\| \mathrm{d}t \\ & \leq n \int_0^1 \sum_{i=0}^{n-1}\left\|B_i^{n-1}(t)(p_{i+1}-p_i)\right\| \mathrm{d}t \\ & = n \sum_{i=0}^{n-1} \int_0^1 B_i^{n-1}(t) \mathrm{d}t \left\|p_{i+1}-p_i\right\| \\ & = \sum_{i=0}^{n-1} \left\|p_{i+1}-p_i\right\| \end{aligned}


  1. 1.但是在下文中我们将会看到, 存在比直接计算多项式更方便的计算 Bézier 曲线的方法.
  2. 2.Prautzsch H, Kobbelt L. Convergence of subdivision and degree elevation[J/OL]. Advances in Computational Mathematics, 1994, 2(1): 143-154. DOI:10.1007/BF02519040.
  3. 3.这里的 $n+1$ 可以替换为任意大于 $n$ 的整数.