一文理解B样条曲线、Nurbs曲线

1,829 阅读4分钟

上一篇文章一文理解贝塞尔曲线讲到,n次贝塞尔曲线实际上就是我们高中学的n次函数:y=a0xn+a1xn1+...+an y = a_0x^n + a_1x^{n-1} + ... + a_{n}。由于贝塞尔曲线的这个特点,也造成了贝塞尔曲线的最大缺陷————不能局部修改,即改变其中一个参数时会改变整条曲线。

为了解决贝塞尔曲线的这个问题,就提出了B样条曲线的方式。它的解决思路很简单,就是分段,把几个曲线拼接在一起,这样我们就可以只修改其中一条曲线,而不会改动其他曲线,从而实现局部修改。

B样条曲线表达式

思路好理解,但是困难的是理解B样条曲线的公式。下面一大坨都是。

P(t)=i=0m1Pibi,n(t),t[tn1,tm+1]{\mathbf {P}}(t)=\sum _{{i=0}}^{{m-1}}{\mathbf {P}}_{{i}}b_{{i,n}}(t){},t\in [t_{n - 1}, t_{m + 1}]
bi,1(t)={1ti<t<ti+10其他bi,n(t)=ttiti+n1tibi,n1(t)+ti+ntti+nti+1bi+1,n1(t)约定00=0b_{{i,1}}(t)=\left\{{\begin{matrix}1&{\mathrm {}}\quad t_{i}<t<t_{{i+1}}\\0&{\mathrm {其他}}\end{matrix}}\right. \\ b_{{i,n}}(t)={\frac {t-t_{{i}}}{t_{{i+n-1}}-t_{i}}}b_{{i,n-1}}(t) + {\frac {t_{{i+n}}-t}{t_{{i+n}}-t_{{i+1}}}}b_{{i+1,n-1}}(t) 约定 \frac{0}{0} = 0

我们先不看这个公式,而是考虑下,如果我们要让一个曲线A,由两条不同的曲线,假设是曲线B、曲线C拼接而成,我们需要什么东西。

首先我们需要了解曲线B、曲线C的函数。那么这个曲线函数由什么确定呢?上文讲过贝塞尔曲线是由控制点来确定的,这里也一样,我们需要控制点来确定曲线B、曲线C的函数。

但是如何分配控制点呢,假设4个控制点abcd,如果是ab、bc、cd控制点,这时候是直线;如果是abc、bcd控制点呢,这时候是曲线,这时候我们就需要确定曲线B、曲线C的系数,即它是几次函数。

其次设置好控制点后,我们还需要决定曲线B、曲线C的范围,比如说[1,2)、[2,3],让它们在2处相交。不然就是两条曲线,而不是一条曲线了。

这时我们再来看第一个公式 P(t)=i=0m1Pibi,n(t),t[tn1,tm+1] {\mathbf {P}}(t)=\sum_{{i=0}}^{{m-1}}{\mathbf {P}}_{{i}}b_{{i,n}}(t){},t\in [t_{n - 1}, t_{m + 1}],其中的参数意义如下:

  • mm:表示控制点的个数
  • PiP_i:表示对应的控制点
  • ii:表示点的系数,P0P_0 P1P_1 P2P_2 P3P_3 分别表示四个控制点
  • nn: 表示B样条曲线的次数,组成的曲线的次数是 n - 1;例如3次B样条曲线,该曲线是由2次函数的曲线拼接起来的
  • ti t_i:表示取值范围,它的数量等于 m + n。例如,m = 4, n = 3时,有7个值,它可以取值是 [0,1,2,3,4,5,6],这时t0=0t_0 = 0t1=1t_1 = 1以此类推;ti t_i也可以取值[0,0,1,2,3,4,5],也可以取小数,只要满足后面的数大于前面的数就可以了
  • bi,nb_{i,n}:是Cox-deBoor 递归公式,这个后面介绍。可以简单理解成多项式的系数

m=4m = 4n=3n = 3ti=[0,1,2,3,4,5,6] t_i = [0,1,2,3,4,5,6]时,我们就可以得到下面的表达式

P(t)=i=03Pibi,3(t),t[t2,t5]展开得到:P(t)=P0b0,3+P1b1,3+P2b2,3+P3b3,3{\mathbf {P}}(t)=\sum_{{i=0}}^{{3}}{\mathbf {P}}_{{i}}b_{{i,3}}(t){},t\in [t_2, t_5] \\ 展开得到:{\mathbf {P}}(t) = P_0b_{0,3} + P_1b_{1,3} + P_2b_{2,3} + P_3b_{3,3}

Cox-deBoor 递归公式

Cox-deBoor 递归公式(公式2)是递推公式,我们需要从i=0,n=1开始,一步步计算出bi,n b_{i,n}的表达式

最简单的是bi,1 b_{i,1},当 i = 0、1、2、3时,它的表达式都是

bi,1(t)={1ti<t<ti+10其他b_{{i,1}}(t)=\left\{{\begin{matrix}1&{\mathrm {}}\quad t_{i}<t<t_{{i+1}}\\0&{\mathrm {其他}}\end{matrix}}\right.

即i为0时

b0,1(t)={1t0<t<t10其他b_{{0,1}}(t)=\left\{{\begin{matrix}1&{\mathrm {}}\quad t_{0}<t<t_{{1}}\\0&{\mathrm {其他}}\end{matrix}}\right.

i为1时

b1,1(t)={1t1<t<t20其他b_{{1,1}}(t)=\left\{{\begin{matrix}1&{\mathrm {}}\quad t_{1}<t<t_{{2}}\\0&{\mathrm {其他}}\end{matrix}}\right.

i=2、3可以依此类推,这里就不继续了。

我们把 n = 2代入,bi,2 b_{i,2} 就等于

bi,2(t)=ttiti+21tibi,21(t)+ti+2tti+2ti+1bi+1,21(t)b_{{i,2}}(t)={\frac {t-t_{{i}}}{t_{{i+2-1}}-t_{i}}}b_{{i,2-1}}(t) + {\frac {t_{{i+2}}-t}{t_{{i+2}}-t_{{i+1}}}}b_{{i+1,2-1}}(t)
化简bi,2(t)=ttiti+1tibi,1(t)+ti+2tti+2ti+1bi+1,1(t)化简b_{{i,2}}(t)={\frac {t-t_{{i}}}{t_{{i+1}}-t_{i}}}b_{{i,1}}(t) + {\frac {t_{{i+2}}-t}{t_{{i+2}}-t_{{i+1}}}}b_{{i+1,1}}(t)

即i为0时

b0,2(t)=tt0t1t0b0,1(t)+t2tt2t1b1,1(t)b_{{0,2}}(t)={\frac {t-t_{{0}}}{t_{{1}}-t_{0}}}b_{{0,1}}(t) + {\frac {t_{{2}}-t}{t_{{2}}-t_{{1}}}}b_{{1,1}}(t)

把上面求得的b0,1(t)b_{{0,1}}(t) b1,1(t) b_{{1,1}}(t) 带入,可以得到

b0,2(t)={tt0t1t0t0<t<t1t2tt2t1t1<t<t20其他b_{{0,2}}(t)=\begin{cases}\frac{t-t_{0}}{t_{1}-t_{0}} & t_{0}<t<t_{1}\\ \frac {t_{{2}}-t}{t_{{2}}-t_{{1}}} & t_{1}<t<t_{{2}}\\ 0 & 其他 \end{cases}

上面我们设置t0=0,t1=1,t2=2t_0 = 0, t_1 = 1 , t_2 = 2,代入可得到

b0,2(t)={t0<t<12t1<t<20其他b_{{0,2}}(t)=\begin{cases}t & 0<t<1\\ 2 - t & 1<t<2\\ 0 & 其他 \end{cases}

类似的我们可以按照相同的方法一步一步的代入,最后就可以求出b0,3b_{0,3}b1,3b_{1,3}b2,3b_{2,3}b3,3b_{3,3}的表达式,这里就不浪费时间推导了。

Nurbs曲线

上面我们把ti=[0,1,2,3,4,5,6] t_i = [0,1,2,3,4,5,6],可以看到每个数之间都相差1;如果我们让它们的差值不相等,即ui+1ui常数u_{i+1} - u_i \neq 常数 时,这种曲线就是Nurbs曲线,也叫做非均匀B样条曲线。

Nurbs曲线和B样条曲线可以简单理解成 C 与 C++ 的关系。C++ 比 C 多了面向对象的能力;而Nurbs曲线比B样条曲线也多了一项能力,就是可以表示圆和椭圆等封闭曲线。

总结

根据上一篇文章一文理解贝塞尔曲线和这一篇文章,我们了解了贝塞尔曲线、B样条曲线、Nurbs曲线。下面我们列一个表格来做对比:

曲线特点缺点
贝塞尔曲线简单、计算快不能局部修改;不能表示圆、椭圆等封闭曲线
B样条曲线可以局部修改不能表示圆、椭圆等封闭曲线
Nurbs曲线可以局部修改;可以表示圆、椭圆等封闭曲线