第4章 B样条曲线:从基函数到分类

B样条曲线,说白了就是贝塞尔曲线的“升级版”。

我刚入行时,用贝塞尔曲线做自由曲面,最头疼的就是——动一个控制点,整条曲线都跟着抖。后来换了B样条,才真正体会到什么叫“局部控制”。这一章,我就带你把这个核心工具吃透。

4.1 B样条基函数的定义

B样条基函数,是整条曲线的“砖瓦”。它不像伯恩斯坦多项式那样全局作用,而是只在局部区间有值。

定义上,我们通常用Cox-de Boor递推公式:

给定节点向量 U = {u₀, u₁, ..., uₘ},p次B样条基函数定义为:

当 p = 0 时:
Nᵢ,₀(u) = 1, 若 uᵢ ≤ u < uᵢ₊₁
Nᵢ,₀(u) = 0, 其他

当 p > 0 时:
Nᵢ,ₚ(u) = (u - uᵢ)/(uᵢ₊ₚ - uᵢ) · Nᵢ,ₚ₋₁(u)
         + (uᵢ₊ₚ₊₁ - u)/(uᵢ₊ₚ₊₁ - uᵢ₊₁) · Nᵢ₊₁,ₚ₋₁(u)

这个公式看着有点吓人,但拆开看其实就两件事:线性插值 + 递归。0次基函数就是个“开关”——在某个区间内是1,外面是0。高次基函数,就是低次基函数的加权组合。

我的经验: 刚开始学的时候,别急着背公式。先画一个节点向量,手动算几个低次基函数的值,画出来看看形状。我当年就是这么干的,画完就理解了。

4.2 B样条基函数的性质

这些性质决定了B样条为什么好用。我挑几个最关键的说说:

  • 局部支撑性: Nᵢ,ₚ(u) 只在区间 [uᵢ, uᵢ₊ₚ₊₁) 上非零。动一个控制点,只影响附近几段曲线。
  • 非负性: 所有基函数值 ≥ 0。这保证了曲线的“凸包性”。
  • 单位分解性: 在任意参数 u 处,所有基函数之和为1。曲线不会“飘”。
  • 连续性: p次B样条基函数在节点处具有 Cᵖ⁻¹ 连续性。节点重复度越高,连续性越低。

避坑指南: 我曾经在项目里用B样条做叶片曲面,节点重复度没处理好,结果曲面在接缝处出现了“鼓包”。后来查了半天才发现——重复度每增加1,连续性就降1阶。这个细节,做工程时一定要算清楚。

4.3 节点向量

节点向量,是B样条曲线的“骨架”。它决定了基函数在参数轴上的分布方式。

一个典型的节点向量长这样:

U = {0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1}

节点向量的长度 m、控制点个数 n+1、曲线次数 p,三者满足:

m = n + p + 2

节点向量里的值,可以重复。重复的节点叫“重节点”。重节点会让基函数在该处的连续性降低。

节点类型 说明 典型用途
简单节点 节点值只出现一次 标准B样条曲线
重节点 节点值出现多次 控制曲线端点或尖角
两端重节点 首尾节点重复 p+1 次 使曲线通过首尾控制点
注意: 节点向量的首尾节点重复次数,决定了曲线是否通过首尾控制点。如果首尾只重复1次,曲线端点不会落在控制点上。这在做曲面拼接时特别容易踩坑。

4.4 B样条曲线的分类

根据节点向量的分布方式,B样条曲线分为三类。我按实际工程中的使用频率来排:

4.4.1 均匀B样条

节点向量中的节点等距分布。比如:

U = {0, 0.2, 0.4, 0.6, 0.8, 1.0}

均匀B样条的基函数形状都一样,只是平移。计算简单,但曲线在两端的行为不太好控制。

我的看法: 均匀B样条适合做快速原型验证。但正式产品里,我很少用它——因为两端不通过控制点,做曲面拼接时很麻烦。

4.4.2 准均匀B样条

节点向量首尾重复 p+1 次,内部节点均匀分布。比如三次准均匀B样条:

U = {0, 0, 0, 0, 0.25, 0.5, 0.75, 1, 1, 1, 1}

这是工程中最常用的类型。它既保留了均匀B样条内部的光滑性,又让曲线通过首尾控制点。

实用技巧: 我做过一个汽车A级曲面项目,所有B样条曲线都用准均匀类型。内部光滑度够,两端又能精确控制边界条件。省了不少后期修面的功夫。

4.4.3 非均匀B样条

节点向量中的节点分布不均匀,且可能有重节点。这是最通用的形式。

非均匀B样条可以做到:

  • 在某个区域集中更多节点,让曲线在该处更“灵活”
  • 通过重节点制造尖角或C⁰连续
  • 精确控制曲线形状的局部细节

说白了,均匀和准均匀都是非均匀的特例。但非均匀的节点向量需要手动设计,对工程师的经验要求更高。

4.5 三类B样条的对比

类型 节点分布 端点行为 计算复杂度 工程适用性
均匀 等距 不通过端点 低 快速原型
准均匀 首尾重复+内部等距 通过端点 中 产品级曲面
非均匀 任意分布 可控制 高 复杂形状

4.6 知识结构图

下面这张图,把B样条曲线的核心知识点串起来了:

B样条曲线知识结构图 B样条曲线知识结构 B样条曲线 B样条基函数 节点向量 曲线分类 Cox-de Boor递推公式 局部支撑性 非负性 + 单位分解性 连续性 Cᵖ⁻¹ 长度关系 m = n + p + 2 简单节点 vs 重节点 两端重复 p+1 次 均匀B样条 准均匀B样条 非均匀B样条 核心要点 局部控制 + 节点向量设计 = B样条的核心优势

4.7 一个简单的代码示例

下面这个Python代码,演示了如何计算三次均匀B样条基函数的值:

import numpy as np

def basis_function(i, p, u, knots):
    """计算第i个p次B样条基函数在u处的值"""
    if p == 0:
        return 1.0 if knots[i] <= u < knots[i+1] else 0.0
    else:
        # 第一项
        denom1 = knots[i+p] - knots[i]
        term1 = 0.0
        if denom1 != 0:
            term1 = (u - knots[i]) / denom1 * basis_function(i, p-1, u, knots)
        
        # 第二项
        denom2 = knots[i+p+1] - knots[i+1]
        term2 = 0.0
        if denom2 != 0:
            term2 = (knots[i+p+1] - u) / denom2 * basis_function(i+1, p-1, u, knots)
        
        return term1 + term2

# 均匀节点向量,三次B样条
knots = [0, 0.2, 0.4, 0.6, 0.8, 1.0]
u = 0.5
for i in range(3):  # 计算前3个基函数
    val = basis_function(i, 3, u, knots)
    print(f"N_{i},3({u}) = {val:.4f}")

工程建议: 实际项目中,别自己手写基函数计算。直接用现成的库,比如 SciPy 的 BSpline 或者 NURBS-Python。但理解递推公式的原理,能帮你调试节点向量时快速定位问题。

嗯,这一章的内容就到这儿。B样条基函数、节点向量、三种分类——这三样东西,是后面学NURBS和自由曲面的基础。你先把它们理清楚,后面就顺了。