2024年11月11日

贝塞尔曲线

贝塞尔曲线用于计算机图形绘制形状,CSS 动画和许多其他地方。

它们其实非常简单,值得学习一次并且在矢量图形和高级动画的世界里非常受用。

控制点

贝塞尔曲线由控制点定义。

这些点可能有 2、3、4 个或更多。

例如,两点曲线:

三点曲线:

四点曲线:

如果仔细观察这些曲线,你会立即注意到:

  1. 控制点不总是在曲线上这是非常正常的,稍后我们将看到曲线是如何构建的。

  2. 曲线的阶次等于控制点的数量减一。 对于两个点我们能得到一条线性曲线(直线),三个点 — 一条二阶曲线,四个点 — 一条三阶曲线。

  3. 曲线总是在控制点的凸包内部:

由于最后一个属性,在计算机图形学中,可以优化相交测试。如果凸包不相交,则曲线也不相交。因此,首先检查凸包的交叉点可以非常快地给出“无交叉”结果。检查交叉区域或凸包更容易,因为它们是矩形,三角形等(见上图),比曲线简单的多。

贝塞尔曲线绘制的主要重点 —— 通过移动曲线,曲线以直观明显的方式变化。

尝试在下面的示例中使用鼠标移动控制点:

可以注意到,曲线沿切线 1 → 2 和 3 → 4 延伸。

经过一些练习后,很明显我们知道怎样通过放置控制点来获得所需要的曲线。通过连接几条曲线,我们几乎可以得到任何东西。

这里有一些例子:

数学

贝塞尔曲线可以使用数学方程式来描述。

很快我们就能看到 —— 没必要知道它。但是为了完整性 —— 请看这里。

给定控制点 Pi 的坐标:第一个控制点的坐标为 P1 = (x1, y1),第二个控制点的坐标为 P2 = (x2, y2),以此类推,曲线坐标由方程式描述,这个方程式依赖属于区间 [0,1] 的参数 t

  • 有两个控制点的曲线方程:

    P = (1-t)P1 + tP2

  • 有三个控制点的曲线方程:

    P = (1−t)2P1 + 2(1−t)tP2 + t2P3

  • 有四个控制点的曲线方程:

    P = (1−t)3P1 + 3(1−t)2tP2 +3(1−t)t2P3 + t3P4

这些是矢量方程。

我们可以逐坐标重写它们,例如 3 点曲线:

  • x = (1−t)2x1 + 2(1−t)tx2 + t2x3
  • y = (1−t)2y1 + 2(1−t)ty2 + t2y3

我们应该放置 3 个控制点的坐标,而不是 x1、y1、x2、y2、x3 和 y3

例如,如果控制点是 (0,0)(0.5, 1)(1, 0),则方程式为:

  • x = (1−t)2 * 0 + 2(1−t)t * 0.5 + t2 * 1 = (1-t)t + t2 = t
  • y = (1−t)2 * 0 + 2(1−t)t * 1 + t2 * 0 = 2(1-t)t = –t2 + 2t

现在随着 t01 变化,每个 t 对应的 (x,y) 集合可以构成曲线。

这可能太学术化了,对于曲线为什么看起来像这样以及它们如何依赖于控制点的描述并不是很明显。

所以绘制算法可能更容易理解。

德卡斯特里奥算法

德卡斯特里奥算法与曲线的数学定义相同,但直观地显示了曲线是如何被建立的。

让我们看看 3 个控制点的例子。

这里是一个演示,随后会有解释。

控制点可以用鼠标移动,点击 “play” 运行演示。

德卡斯特里奥算法构造三点贝塞尔曲线:

  1. 绘制控制点。在上面的演示中,它们标有:123

  2. 创建控制点 1 → 2 → 3 间的线段. 在上面的演示中它们是棕色的。

  3. 参数 t0 to 1 变化。 在上面的演示中取值 0.05:循环遍历 0, 0.05, 0.1, 0.15, ... 0.95, 1

    对于每一个 t 的取值:

    • 在每一个棕色线段上我们取一个点,这个点距起点的距离按比例 t 取值。由于有两条线段,我们能得到两个点。

      例如,当 t=0 — 所有点都在线段起点处,当 t=0.25 — 点到起点的距离为线段长度的 25%,当 t=0.5 — 50%(中间),当 t=1 — 线段终点。

    • 连接这些点,下面这张图中连好的线被绘制成蓝色

t=0.25 t=0.5
  1. 现在在蓝色线段上取一个点,距离比例取相同数值的 t。也就是说,当 t=0.25(左图)时,我们取到的点位于线段的左 1/4 终点处,当 t=0.5(右图)时 — 线段中间。在上图中这一点是红色的。

  2. 随着 t0 to 1 变化,每一个 t 的值都会添加一个点到曲线上。这些点的集合就形成的贝塞尔曲线。它在上面的图中是红色的,并且是抛物线状的。

这是三控制点的处理过程,但是对于 4 个点同样适用。

4 个控制点的演示(点可以被鼠标移动):

算法:

  • 控制点通过线段连接:1 → 2、2 → 3 和 3 → 4。 我们能得到 3 条棕色的线段。
  • 对于 0 to 1 之间的每一个 t
    • 我们在这些线段上距起点距离比例为 t 的位置取点。把这些点连接起来,然后得到两条绿色线段
    • 在这些线段上同样按比例 t 取点,得到一条蓝色线段
    • 在蓝色线段按比例 t 取点。在上面的例子中是红色的。
  • 这些点在一起组成了曲线。

该算法是递归的,并且可以适应于任意数量的控制点。

给定 N 个控制点,我们将它们连接起来以获得初始的 N-1 个线段。

然后对从 01 的每一个 t

  • 在每条线段上按 t 比例距离取一个点并且连接 —— 会得到 N-2 个线段。
  • 在上面得到的每条线段上按 t 比例距离取一个点并且连接 —— 会得到 N-3 个线段,以此类推……
  • 直到我们得到一个点。得到的这些点就形成了曲线。

曲线的移动演示:

和其它的点:

环形:

非平滑贝塞尔曲线:

由于算法是递归的,我们可以构建任何顺序的贝塞尔曲线:使用 5 个、6 个或更多个控制点。但在实践中它们没那么有用。通常我们取 2-3 个点,对于复杂的线条,将几条曲线拼接在一起。这更容易开发和计算。

如何通过给定点绘制曲线?

我们使用控制点制作贝塞尔曲线。正如我们所见,它们并不在曲线上。或者更准确地说,第一个和最后一个在曲线上,但其它的不在。

有时我们有另一种任务:绘制一条曲线通过几个点,让它们都在一条平滑曲线上。这种任务叫插值,这里我们不覆盖讲解它。

这些曲线有数学方程式,例如拉格朗日多项式

在计算机图形中样条插值通常用于构建连接多个点的平滑曲线。

总结

贝塞尔曲线由其控制点定义。

贝塞尔曲线的两种定义方法:

  1. 使用数学方程式。
  2. 使用绘图过程:德卡斯特里奥算法

贝塞尔曲线的优点:

  • 我们可以通过控制点移动来用鼠标绘制平滑线条。
  • 复杂的形状可以由多条贝塞尔曲线组成。

用途:

  • 在计算机图形学,建模,矢量图形编辑器中。字体由贝塞尔曲线描述。
  • 在 Web 开发中 — 用于 Canvas 上的图形和 SVG 格式。顺便说一下,上面的“实时”示例是用 SVG 编写的。它们实际上是一个 SVG 文档,被赋予不同的控制点做参数。你可以在单独的窗口中打开它并查源码:demo.svg
  • 在 CSS 动画中描述动画的路径和速度。
教程路线图

评论

在评论之前先阅读本内容…
  • 如果你发现教程有错误,或者有其他需要修改和提升的地方 — 请 提交一个 GitHub issue 或 pull request,而不是在这评论。
  • 如果你对教程的内容有不理解的地方 — 请详细说明。
  • 使用 <code> 标签插入只有几个词的代码,插入多行代码可以使用 <pre> 标签,对于超过 10 行的代码,建议你使用沙箱(plnkrJSBincodepen…)