B样条曲线 #
在学习曲线建模时,Bézier 曲线通常是最先接触的一类曲线。它的形式简洁,几何意义直观,非常适合用来理解“控制点如何影响曲线形状”。
但是当曲线形状变得复杂时,单条 Bézier 曲线会暴露出一些明显问题。B-Spline,也就是 B 样条曲线,正是为了解决这些问题而出现的。
本文主要整理两个问题:
- 为什么需要 B-Spline?它解决了什么问题?
- B-Spline 的数学定义是什么?
1. 为什么需要 B-Spline? #
1.1 Bézier 曲线的基本形式 #
一条 n 次 Bézier 曲线可以写成:
C(t)=i=0∑nBi,n(t)Pi其中:
Bi,n(t)=(in)(1−t)n−iti这里:
- Pi 是控制点;
- Bi,n(t) 是 Bernstein 基函数;
- t 是曲线参数,通常满足 0≤t≤1。
从形式上看,Bézier 曲线就是对控制点做加权求和:
曲线点=∑基函数权重×控制点这个形式非常重要,因为 B-Spline 后面也采用类似结构。
1.2 Bézier 曲线的问题一:控制点数量决定曲线次数 #
Bézier 曲线有一个特点:
曲线次数=控制点数量−1例如:
- 4 个控制点对应 3 次 Bézier 曲线;
- 6 个控制点对应 5 次 Bézier 曲线;
- 11 个控制点对应 10 次 Bézier 曲线。
这会带来一个问题:
如果我们为了描述更复杂的形状而增加控制点,曲线次数也会随之升高。
高次数曲线通常有几个缺点:
第一,计算更复杂。 曲线点、导数、曲率等计算都会变得更麻烦。
第二,曲线更难控制。 高次曲线容易产生不必要的摆动或振荡。
第三,整体影响更强。 修改一个控制点时,可能会影响整条曲线,而不是只影响局部区域。
因此,Bézier 曲线在简单形状中很好用,但当曲线变得复杂时,单条高次 Bézier 曲线就不太方便。
1.3 Bézier 曲线的问题二:缺少局部控制性 #
Bézier 曲线还有一个重要问题:它是整体控制的。
也就是说,移动某一个控制点时,通常会影响整条曲线。
如果我们只想修改曲线中间的一小段,比如让花瓶的瓶颈部分更细一点,使用单条 Bézier 曲线会比较麻烦。因为调整某个控制点时,曲线其他部分也会受到影响。
这在复杂曲线设计中非常不方便。
1.4 一种改进思路:使用多段低次 Bézier 曲线 #
为了避免使用一条高次 Bézier 曲线,可以把复杂曲线拆成多段低次 Bézier 曲线。
例如,不用一条 11 次 Bézier 曲线,而是使用几段 3 次 Bézier 曲线拼接起来。
这样做的好处是:
- 每一段曲线次数较低;
- 整体曲线仍然可以表达复杂形状;
- 局部修改更方便。
但是这种方法又带来一个新的问题:拼接处的连续性需要手动处理。
比如两段曲线拼接时,如果希望连接处看起来没有尖角,就需要满足切线方向一致,也就是 G1 连续。
如果希望更严格一些,还要满足导数连续,也就是 C1 连续。
手动维护这些连续性条件会比较麻烦。
1.5 B-Spline 的核心思想 #
B-Spline 可以看成是对这个问题的进一步解决:
使用多段低次数曲线拼接出复杂形状,同时自动维护一定的连续性。
B-Spline 的核心思想是:
- 不再用一条高次曲线描述整个形状;
- 而是用多段低次多项式曲线拼接;
- 每一段曲线通常次数较低,例如 2 次、3 次;
- 曲线整体仍然可以表达复杂形状;
- 控制点主要影响局部区域,而不是整条曲线。
因此,B-Spline 解决了 Bézier 曲线的两个主要问题:
第一,控制点数量和曲线次数解耦。 也就是说,可以有很多控制点,但仍然保持较低次数。
第二,具有局部控制性。 修改一个控制点时,通常只影响曲线的一小段区域。
这就是 B-Spline 相比 Bézier 曲线更适合复杂建模的原因。
2. B-Spline 的定义 #
2.1 B-Spline 曲线的形式 #
给定 n+1 个控制点:
P0,P1,…,Pn给定一个节点向量:
U={u0,u1,…,um}以及曲线次数 p,那么一条 p 次 B-Spline 曲线定义为:
C(u)=i=0∑nNi,p(u)Pi其中:
- C(u) 是曲线上的点;
- u 是参数;
- Pi 是第 i 个控制点;
- Ni,p(u) 是第 i 个 p 次 B-Spline 基函数;
- p 是曲线次数。
这个公式和 Bézier 曲线的形式很像。
Bézier 曲线是:
C(t)=i=0∑nBi,n(t)PiB-Spline 曲线是:
C(u)=i=0∑nNi,p(u)Pi二者都是:
曲线点=∑基函数权重×控制点区别在于:
- Bézier 使用 Bernstein 基函数 Bi,n(t);
- B-Spline 使用 B-Spline 基函数 Ni,p(u)。
2.2 B-Spline 基函数的含义 #
在公式
C(u)=i=0∑nNi,p(u)Pi中,Ni,p(u) 可以理解为:
当参数为 u 时,第 i 个控制点 Pi 对曲线点 C(u) 的影响权重。
例如,某个参数 u 处只有几个基函数非零,那么曲线点只会由这几个控制点共同决定。
这就是 B-Spline 局部控制性的来源。
Bézier 曲线中,Bernstein 基函数通常在整个参数区间上都有影响,因此控制点会影响整条曲线。
而 B-Spline 基函数通常只在某一小段参数区间内非零,因此控制点只影响局部曲线。
2.3 B-Spline 基函数的递归定义 #
B-Spline 基函数通常不是像 Bernstein 基函数那样直接写成一个简单公式,而是用递归方式定义。
这个递归定义通常称为 Cox-de Boor 递归公式。
首先定义 0 次基函数:
Ni,0(u)={1,0,ui≤u<ui+1otherwise意思是:
如果参数 u 落在区间 [ui,ui+1) 内,那么 Ni,0(u)=1;否则为 0。
然后递归定义 p 次基函数:
Ni,p(u)=ui+p−uiu−uiNi,p−1(u)+ui+p+1−ui+1ui+p+1−uNi+1,p−1(u)如果分母为 0,则对应项通常记为 0。
这个公式的含义是:
高一次的 B-Spline 基函数,是由两个低一次的 B-Spline 基函数组合而成的。
例如:
- Ni,1(u) 由 0 次基函数构造;
- Ni,2(u) 由 1 次基函数构造;
- Ni,3(u) 由 2 次基函数构造。
所以 B-Spline 基函数是逐层递归生成的。
2.4 节点向量 Knot Vector #
B-Spline 与 Bézier 的一个重要区别是:B-Spline 需要节点向量。
节点向量写作:
U={u0,u1,…,um}其中每个 ui 叫做 knot,也就是节点。
节点向量的作用是划分参数区间。
例如,如果参数区间被节点分成若干小区间:
[ui,ui+1]那么每个小区间就叫做一个 knot span。
B-Spline 曲线正是在这些 knot span 上分段定义的。
因此,B-Spline 可以理解为:
一条由多个低次数多项式曲线段拼接而成的曲线。
每个 knot span 对应曲线的一小段。
2.5 n、m、p 的关系 #
定义 B-Spline 时,控制点数量、节点数量和曲线次数不能随便给,它们必须满足一个固定关系:
m=n+p+1这里:
- 控制点数量是 n+1;
- 节点数量是 m+1;
- 曲线次数是 p。
因此也可以写成:
节点数量=控制点数量+p+1因为:
m+1=(n+1)+p+1举个例子:
如果有 10 个控制点,那么:
n+1=10所以:
n=9如果曲线次数是 3,即:
p=3那么:
m=n+p+1=9+3+1=13节点数量是:
m+1=14所以,10 个控制点的三次 B-Spline 曲线需要 14 个节点。
2.6 次数不能超过控制点数量限制 #
除了满足
m=n+p+1之外,还需要满足:
p≤n也就是说:
曲线次数≤控制点数量−1例如,如果只有 3 个控制点,那么:
n+1=3所以:
n=2这时最多只能定义 2 次曲线,不能定义 5 次 B-Spline。
因此,像“5 次 B-Spline,10 个节点,3 个控制点”这样的组合是不合法的。
因为 3 个控制点最多只能支持 2 次曲线,不可能定义 5 次曲线。
2.7 Open、Clamped 和 Closed B-Spline #
根据节点向量的不同,B-Spline 曲线可以有不同类型。
Open B-Spline #
普通的 open B-Spline 不一定经过第一个控制点和最后一个控制点。
它的首尾也不一定与控制多边形的首尾边相切。
Clamped B-Spline #
如果希望 B-Spline 像 Bézier 曲线一样:
- 从第一个控制点开始;
- 到最后一个控制点结束;
- 起点处与第一条控制边相切;
- 终点处与最后一条控制边相切;
就可以使用 clamped B-Spline。
做法是让节点向量的首尾节点重复 (p+1) 次。
例如三次 B-Spline 中:
p=3因此首尾节点各重复:
p+1=4次。
例如:
U={0,0,0,0,0.25,0.5,0.75,1,1,1,1}这就是一个 clamped knot vector。
Closed B-Spline #
如果希望曲线首尾相接,形成闭合曲线,就可以构造 closed B-Spline。
这种曲线常用于轮廓、环形曲线、封闭路径等场景。
3. B-Spline 的核心性质 #
B-Spline 之所以有用,是因为它的基函数具有一些重要性质。
3.1 非负性 #
Ni,p(u)≥0基函数不会产生负权重。
3.2 权重和为 1 #
在有效参数区间内,B-Spline 基函数满足:
i∑Ni,p(u)=1因此曲线点可以看成控制点的加权平均。
这保证了曲线不会随意偏离控制点形成的区域。
3.3 局部支撑 #
每个基函数 (N_{i,p}(u)) 只在有限的参数区间内非零。
这意味着每个控制点只影响曲线的一部分,而不是整条曲线。
这正是 B-Spline 具有局部控制性的根本原因。
4. 总结 #
B-Spline 可以看成是 Bézier 曲线的一种推广。
Bézier 曲线的主要问题是:
- 控制点数量决定曲线次数;
- 控制点对整条曲线产生全局影响;
- 复杂形状容易导致高次曲线;
- 多段 Bézier 拼接时需要手动处理连续性。
B-Spline 解决这些问题的方式是:
- 使用低次数的分段多项式;
- 通过 knot vector 控制参数区间分段;
- 使用 B-Spline 基函数作为控制点权重;
- 让控制点数量和曲线次数解耦;
- 提供局部控制能力;
- 自动保证曲线段之间的一定连续性。
B-Spline 曲线的定义是:
C(u)=i=0∑nNi,p(u)Pi其中:
- Pi 是控制点;
- Ni,p(u) 是 B-Spline 基函数;
- p 是曲线次数;
- U=u0,u1,…,um 是节点向量。
并且三者之间满足:
m=n+p+1或者:
节点数量=控制点数量+曲线次数+1一句话概括:
B-Spline 是一种通过节点向量和局部基函数,把多个低次数曲线段平滑拼接起来的曲线表示方法。它既能表达复杂形状,又能保持良好的局部控制性。
(完)