日期:2026年3月14日标签:Computer Graphics

B样条曲线 #

在学习曲线建模时,Bézier 曲线通常是最先接触的一类曲线。它的形式简洁,几何意义直观,非常适合用来理解“控制点如何影响曲线形状”。

但是当曲线形状变得复杂时,单条 Bézier 曲线会暴露出一些明显问题。B-Spline,也就是 B 样条曲线,正是为了解决这些问题而出现的。

本文主要整理两个问题:

  1. 为什么需要 B-Spline?它解决了什么问题?
  2. B-Spline 的数学定义是什么?

1. 为什么需要 B-Spline? #

1.1 Bézier 曲线的基本形式 #

一条 nn 次 Bézier 曲线可以写成:

C(t)=i=0nBi,n(t)PiC(t)=\sum_{i=0}^{n}B_{i,n}(t)P_i

其中:

Bi,n(t)=(ni)(1t)nitiB_{i,n}(t)=\binom{n}{i}(1-t)^{n-i}t^i

这里:

  • PiP_i 是控制点;
  • Bi,n(t)B_{i,n}(t) 是 Bernstein 基函数;
  • tt 是曲线参数,通常满足 0t10 \leq t \leq 1

从形式上看,Bézier 曲线就是对控制点做加权求和:

曲线点=基函数权重×控制点\text{曲线点}=\sum \text{基函数权重}\times \text{控制点}

这个形式非常重要,因为 B-Spline 后面也采用类似结构。


1.2 Bézier 曲线的问题一:控制点数量决定曲线次数 #

Bézier 曲线有一个特点:

曲线次数=控制点数量1\text{曲线次数}=\text{控制点数量}-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 曲线拼接起来。

这样做的好处是:

  • 每一段曲线次数较低;
  • 整体曲线仍然可以表达复杂形状;
  • 局部修改更方便。

但是这种方法又带来一个新的问题:拼接处的连续性需要手动处理。

比如两段曲线拼接时,如果希望连接处看起来没有尖角,就需要满足切线方向一致,也就是 G1G^1 连续。

如果希望更严格一些,还要满足导数连续,也就是 C1C^1 连续。

手动维护这些连续性条件会比较麻烦。


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+1n+1 个控制点:

P0,P1,,PnP_0,P_1,\ldots,P_n

给定一个节点向量:

U={u0,u1,,um}U=\{u_0,u_1,\ldots,u_m\}

以及曲线次数 pp,那么一条 pp 次 B-Spline 曲线定义为:

C(u)=i=0nNi,p(u)PiC(u)=\sum_{i=0}^{n}N_{i,p}(u)P_i

其中:

  • C(u)C(u) 是曲线上的点;
  • uu 是参数;
  • PiP_i 是第 ii 个控制点;
  • Ni,p(u)N_{i,p}(u) 是第 iipp 次 B-Spline 基函数;
  • pp 是曲线次数。

这个公式和 Bézier 曲线的形式很像。

Bézier 曲线是:

C(t)=i=0nBi,n(t)PiC(t)=\sum_{i=0}^{n}B_{i,n}(t)P_i

B-Spline 曲线是:

C(u)=i=0nNi,p(u)PiC(u)=\sum_{i=0}^{n}N_{i,p}(u)P_i

二者都是:

曲线点=基函数权重×控制点\text{曲线点}=\sum \text{基函数权重}\times \text{控制点}

区别在于:

  • Bézier 使用 Bernstein 基函数 Bi,n(t)B_{i,n}(t)
  • B-Spline 使用 B-Spline 基函数 Ni,p(u)N_{i,p}(u)

2.2 B-Spline 基函数的含义 #

在公式

C(u)=i=0nNi,p(u)PiC(u)=\sum_{i=0}^{n}N_{i,p}(u)P_i

中,Ni,p(u)N_{i,p}(u) 可以理解为:

当参数为 uu 时,第 ii 个控制点 PiP_i 对曲线点 C(u)C(u) 的影响权重。

例如,某个参数 uu 处只有几个基函数非零,那么曲线点只会由这几个控制点共同决定。

这就是 B-Spline 局部控制性的来源。

Bézier 曲线中,Bernstein 基函数通常在整个参数区间上都有影响,因此控制点会影响整条曲线。

而 B-Spline 基函数通常只在某一小段参数区间内非零,因此控制点只影响局部曲线。


2.3 B-Spline 基函数的递归定义 #

B-Spline 基函数通常不是像 Bernstein 基函数那样直接写成一个简单公式,而是用递归方式定义。

这个递归定义通常称为 Cox-de Boor 递归公式。

首先定义 0 次基函数:

Ni,0(u)={1,uiu<ui+10,otherwiseN_{i,0}(u)= \begin{cases} 1, & u_i \le u < u_{i+1} \\ 0, & \text{otherwise} \end{cases}

意思是:

如果参数 uu 落在区间 [ui,ui+1)[u_i,u_{i+1}) 内,那么 Ni,0(u)=1N_{i,0}(u)=1;否则为 0。

然后递归定义 pp 次基函数:

Ni,p(u)=uuiui+puiNi,p1(u)+ui+p+1uui+p+1ui+1Ni+1,p1(u)N_{i,p}(u)= \frac{u-u_i}{u_{i+p}-u_i}N_{i,p-1}(u) + \frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)

如果分母为 0,则对应项通常记为 0。

这个公式的含义是:

高一次的 B-Spline 基函数,是由两个低一次的 B-Spline 基函数组合而成的。

例如:

  • Ni,1(u)N_{i,1}(u) 由 0 次基函数构造;
  • Ni,2(u)N_{i,2}(u) 由 1 次基函数构造;
  • Ni,3(u)N_{i,3}(u) 由 2 次基函数构造。

所以 B-Spline 基函数是逐层递归生成的。


2.4 节点向量 Knot Vector #

B-Spline 与 Bézier 的一个重要区别是:B-Spline 需要节点向量。

节点向量写作:

U={u0,u1,,um}U=\{u_0,u_1,\ldots,u_m\}

其中每个 uiu_i 叫做 knot,也就是节点。

节点向量的作用是划分参数区间。

例如,如果参数区间被节点分成若干小区间:

[ui,ui+1][u_i,u_{i+1}]

那么每个小区间就叫做一个 knot span。

B-Spline 曲线正是在这些 knot span 上分段定义的。

因此,B-Spline 可以理解为:

一条由多个低次数多项式曲线段拼接而成的曲线。

每个 knot span 对应曲线的一小段。


2.5 n、m、p 的关系 #

定义 B-Spline 时,控制点数量、节点数量和曲线次数不能随便给,它们必须满足一个固定关系:

m=n+p+1m=n+p+1

这里:

  • 控制点数量是 n+1n+1
  • 节点数量是 m+1m+1
  • 曲线次数是 pp

因此也可以写成:

节点数量=控制点数量+p+1\text{节点数量}=\text{控制点数量}+p+1

因为:

m+1=(n+1)+p+1m+1=(n+1)+p+1

举个例子:

如果有 10 个控制点,那么:

n+1=10n+1=10

所以:

n=9n=9

如果曲线次数是 3,即:

p=3p=3

那么:

m=n+p+1=9+3+1=13m=n+p+1=9+3+1=13

节点数量是:

m+1=14m+1=14

所以,10 个控制点的三次 B-Spline 曲线需要 14 个节点。


2.6 次数不能超过控制点数量限制 #

除了满足

m=n+p+1m=n+p+1

之外,还需要满足:

pnp \leq n

也就是说:

曲线次数控制点数量1\text{曲线次数} \leq \text{控制点数量}-1

例如,如果只有 3 个控制点,那么:

n+1=3n+1=3

所以:

n=2n=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=3p=3

因此首尾节点各重复:

p+1=4p+1=4

次。

例如:

U={0,0,0,0,0.25,0.5,0.75,1,1,1,1}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)0N_{i,p}(u)\geq 0

基函数不会产生负权重。

3.2 权重和为 1 #

在有效参数区间内,B-Spline 基函数满足:

iNi,p(u)=1\sum_i N_{i,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=0nNi,p(u)PiC(u)=\sum_{i=0}^{n}N_{i,p}(u)P_i

其中:

  • PiP_i 是控制点;
  • Ni,p(u)N_{i,p}(u) 是 B-Spline 基函数;
  • pp 是曲线次数;
  • U=u0,u1,,umU={u_0,u_1,\ldots,u_m} 是节点向量。

并且三者之间满足:

m=n+p+1m=n+p+1

或者:

节点数量=控制点数量+曲线次数+1\text{节点数量}=\text{控制点数量}+\text{曲线次数}+1

一句话概括:

B-Spline 是一种通过节点向量和局部基函数,把多个低次数曲线段平滑拼接起来的曲线表示方法。它既能表达复杂形状,又能保持良好的局部控制性。

(完)

目录