文章目录

  • 资料援引
  • 贝塞尔曲线的用途
  • 一阶贝塞尔(bezier)曲线
  • 二阶贝塞尔(bezier)曲线
  • 三阶贝塞尔(bezier)曲线
  • 高阶贝塞尔(bezier)曲线
  • 三阶贝塞尔曲线求插值(Slerp)

资料援引

B站视频:wow,神奇的贝塞尔曲线!

博客:贝塞尔曲线简单介绍

知乎:曲线篇: 贝塞尔曲线


贝塞尔曲线的用途

  • 基于对汽车的的车身结构进行流体化设计而诞生
  • 处理视频状态点之间的图像变化
  • 随心所欲绘制曲线,比如:


一阶贝塞尔(bezier)曲线

如上,P0P_0P0​、P1P_1P1​ 两点构成了一条线段,而我们可以通过一个函数——线性插值(lerp),来根据一个 ttt 值(t∈[0,1]t \in [0,1]t∈[0,1]) 得到线段上一点 PPP(图中一直在滑动的点)。而 PPP 的运动轨迹(红线),便是一阶贝塞尔线段(曲线)。线性插值的数学形式(一阶贝塞尔曲线公式)为:

P=lerp(P0,P1,t)=(1−t)P0+tP1P=lerp(P_0,P_1,t)=(1-t)P_0 + tP_1P=lerp(P0​,P1​,t)=(1−t)P0​+tP1​

一阶贝塞尔曲线有两个端点(P0P_0P0​、P1P_1P1​ ),0个控制点


二阶贝塞尔(bezier)曲线

如上,假设现在有点 P2P_2P2​ ,它与 P1P_1P1​ 构成了新的线段,我们得到两个 一阶插值点(Q1Q_1Q1​、Q2Q_2Q2​),它们构成了绿色线段,值得注意的是,两个插值点具有相同的 ttt 值。

而此时我们在绿色线段上生成一个 二阶插值点(PPP),并让它具有 与两个一阶插值点相同的ttt 值。 那么该点的运动轨迹就是 二阶贝塞尔曲线。其公式推导为:

  • 绿色线段左端点的运动轨迹:

Q1=(1−t)P0+tP1Q_1 = (1-t)P_0 + tP_1 Q1​=(1−t)P0​+tP1​

  • 绿色线段右端点的运动轨迹:

Q2=(1−t)P1+tP2Q_2 = (1-t)P_1 + tP_2Q2​=(1−t)P1​+tP2​

  • 二阶贝塞尔曲线公式:

P=(1−t)Q1+tQ2P = (1-t)Q_1 + tQ_2P=(1−t)Q1​+tQ2​
=(1−t)((1−t)P0+tP1)+t((1−t)P1+tP2)=(1-t)((1-t)P_0 + tP_1) + t((1-t)P_1 + tP_2)=(1−t)((1−t)P0​+tP1​)+t((1−t)P1​+tP2​)
=(1−t)2P0+2t(t−1)P1+t2P2=(1-t)^2P_0+2t(t-1)P_1+t^2P_2=(1−t)2P0​+2t(t−1)P1​+t2P2​

二阶贝塞尔曲线有两个端点(P0P_0P0​、P2P_2P2​),一个控制点(P1P_1P1​)。


三阶贝塞尔(bezier)曲线

经过对一阶、二阶贝塞尔曲线的研究学习,我们能知道贝塞尔曲线通过在两点之间再采点的方式实现降阶,每一次选点都是一次的降阶。

  • P0P_0P0​、P1P_1P1​、P2P_2P2​、P3P_3P3​ 通过生成插值点 Q1Q_1Q1​、Q2Q_2Q2​、Q3Q_3Q3​ 来构成二阶贝塞尔(绿色线段)
  • 在此基础上生成插值点 O1O_1O1​、O2O_2O2​ 来构成一阶贝塞尔(蓝色线段)
  • 之后以 O1O_1O1​、O2O_2O2​ 上的插值点 PPP 的运动轨迹来生成三阶贝塞尔曲线。

公式推导过程同二阶贝塞尔曲线,因此不做赘述,直接贴出公式:

P=(1−t)3P0+3t(1−t)2P1+3t2(1−t)P2+t3P3P=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3P=(1−t)3P0​+3t(1−t)2P1​+3t2(1−t)P2​+t3P3​

三阶贝塞尔曲线有两个端点(P0P_0P0​、P3P_3P3​),两个控制点(P1P_1P1​、P2P_2P2​)。


高阶贝塞尔(bezier)曲线

  • 四阶贝塞尔曲线示意图:

  • 五阶贝塞尔曲线示意图:

  • 高阶贝塞尔曲线公式:

P(t)=∑i=0nPiBi,n(t),t∈[0,1]P(t)=\sum_{i=0}^{n}P_iB_{i,n}(t),t \in [0,1]P(t)=i=0∑n​Pi​Bi,n​(t),t∈[0,1]

Bi,n(t)=Cniti(1−t)n−i=n!i!(n−i)!ti(1−t)n−i,【i=0,1,...,n】B_{i,n}(t)=C_n^it^i(1-t)^{n-i}=\frac{n!}{i!(n-i)!}t^i(1-t)^{n-i},【i=0,1,...,n】Bi,n​(t)=Cni​ti(1−t)n−i=i!(n−i)!n!​ti(1−t)n−i,【i=0,1,...,n】


三阶贝塞尔曲线求插值(Slerp)

在熟悉了贝塞尔曲线的相关概念之后,我们来了解一下它的具体应用。通常它的应用场景是:

已知两个端点和两个控制点的情况下,根据 动画进度向量 PxP_xPx​ 求 ttt,再由 ttt 确认的曲线求 PyP_yPy​。

回顾一下三阶贝塞尔曲线公式:
P=(1−t)3P0+3t(1−t)2P1+3t2(1−t)P2+t3P3P=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3P=(1−t)3P0​+3t(1−t)2P1​+3t2(1−t)P2​+t3P3​

公式中的 P0P_0P0​、P1P_1P1​ 等都是二维向量,由两个一维向量 PxP_xPx​ 和 PyP_yPy​ 构成。而我们根据 ttt 求 PPP,本质上是根据 ttt 来求一个坐标 (x,y)(x,y)(x,y)。因此,可将公式拆解在两个一维向量上:
y=(1−t)3Py0+3t(1−t)2Py1+3t2(1−t)Py2+t3Py3y=(1-t)^3P_{y0}+3t(1-t)^2P_{y1}+3t^2(1-t)P_{y2}+t^3P_{y3}y=(1−t)3Py0​+3t(1−t)2Py1​+3t2(1−t)Py2​+t3Py3​

x=(1−t)3Px0+3t(1−t)2Px1+3t2(1−t)Px2+t3Px3x=(1-t)^3P_{x0}+3t(1-t)^2P_{x1}+3t^2(1-t)P_{x2}+t^3P_{x3}x=(1−t)3Px0​+3t(1−t)2Px1​+3t2(1−t)Px2​+t3Px3​

而由于我们在处理动画时通常起点 P0P_0P0​ 和终点 P3P_3P3​ 都是可以确定的【P0(0,0)、P3(1,1)P_0(0,0)、P_3(1,1)P0​(0,0)、P3​(1,1)】,因此上述公式可以化简为(以 xxx 举例,yyy 同理):

x=3t(1−t)2Py1+3t2(1−t)Py2+t3x=3t(1-t)^2P_{y1}+3t^2(1-t)P_{y2}+t^3x=3t(1−t)2Py1​+3t2(1−t)Py2​+t3

完全展开:
=3Py1t−6Py1t2+3Py1t3+3Py2t2−3Py2t3+t3=3P_{y1}t-6P_{y1}t^2+3P_{y1}t^3+3P_{y2}t^2-3P_{y2}t^3+t^3=3Py1​t−6Py1​t2+3Py1​t3+3Py2​t2−3Py2​t3+t3

提取三次方系数 aaa:
a=3Py1−3Py2+1a=3P_{y1}-3P_{y2}+1a=3Py1​−3Py2​+1

提取二次方系数 bbb:
b=3Py2−6Py1b=3P_{y2}-6P_{y1}b=3Py2​−6Py1​

提取一次方系数 ccc:
c=3Py1c=3P_{y1}c=3Py1​

将公式简化为:
x=at3+bt2+ctx=at^3+bt^2+ctx=at3+bt2+ct

移动 xxx,将公式变为一元三次方程:
at3+bt2+ct−x=0at^3+bt^2+ct-x=0at3+bt2+ct−x=0

此时,就可以通过卡尔丹公式根据 xxx 求出来 ttt。之后根据 ttt 可以求得 yyy:
y=(1−t)3Py0+3t(1−t)2Py1+3t2(1−t)Py2+t3Py3y=(1-t)^3P_{y0}+3t(1-t)^2P_{y1}+3t^2(1-t)P_{y2}+t^3P_{y3}y=(1−t)3Py0​+3t(1−t)2Py1​+3t2(1−t)Py2​+t3Py3​

代码实现:

double SlerpWithCubicBazier(double pX1, double pY1, double pX2, double pY2, double x) {// x为动画进度,并不是t,t只是一个参数,先根据x求t,再由t确认的曲线上求y// 参考 https://github.com/gre/bezier-easing/blob/master/src/index.jsdouble t = 0.0;if (x <= 0.0) {t = 0.0;}else if (x >= 1.0) {t = 1.0;}else {// x = (1-t)^3*P0x + 3*(1-t)^2*t*P1x + 3*(1-t)*t^2*P2x + t^3*P3x// 提取系数:double a = 0.0 + 3 * pX1 - 3 * pX2 + 1.0;double b = 3 * 0.0 - 6 * pX1 + 3 * pX2;double c = 0.0 + 3 * pX1;// 公式可化简为: x = at^3 + bt^2 + ct// 转换为基于 t 的一元三次方程:at^3 + bt^2 + ct - x = 0double d = 0 - x;// 那么就可以通过 SolveCubic 函数根据a、b、c、d四个系数来求解一元三次方程的一个实根// 可能该一元三次方程的根不止一个,但不重要,即使有多个根我们也只需要其中之一,且要求这个根是在 0~1 之间的,符合 t 的取值范围要求,如果没有根/没有符合要求的根我们会返回 -1double tTemp = SolveCubic(a, b, c, d);if (tTemp == -1) {return -1;}t = tTemp;}// Gy(t) = P0*(1-t)^3 + 3*P1*t*(1-t)^2 + 3*P2*t^2*(1-t) + P3*t^3  t[0,1]// PY0=0.0 PY3=1.0double coef1 = 0.0 * (1.0 - t) * (1.0 - t) * (1.0 - t);double coef2 = pY1 * 3 * t * (1.0 - t) * (1.0 - t);double coef3 = pY2 * 3 * t * t *  (1.0 - t);double coef4 = 1.0 * t * t * t;double gt = coef1 + coef2 + coef3 + coef4;return gt;
}

SolveCubic 函数的具体实现如下,值得注意的是,并不能简单的将此函数的作用等同于求解一元三次方程,本函数的本质作用是贝塞尔曲线中根据 x 求出 t,这两者有什么区别呢?举个具体的例子,下面的代码第 5 行有这样的语句:

if (d == 0) return 0;
  • 在 d=0 的情况下,普通一元三次方程是可以继续求解的;
  • 但是在 SlerpWithCubicBazier 调用 SolveCubic 时,是以 d=0−xd=0-xd=0−x 的形式传值的,d=0d=0d=0 代表 x=0x=0x=0,此时曲线位于起点,t 的值可以确定,无需通过解方程获得,即 t=0t=0t=0。
double FCPSolveCubic(double a, double b, double c, double d) {/* a=0 视为一元二次方程式 */if (a == 0) return FCPSolveQuadratic(b, c, d);/* d=0表明x=0,则t=0*/if (d == 0) return 0;/* 将三次方的系数变为1,方便后续判别式中的计算,即不用考虑 a^2 这一项了 */b /= a;c /= a;d /= a;/* q和r对应求根公式中的p和q,dis即是求根公式的判别式 △ */double q = (3.0 * c - FCPSquared(b)) / 9.0;double r = (-27.0 * d + b * (9.0 * c - 2.0 * FCPSquared(b))) / 54.0;double disc = FCPCubed(q) + FCPSquared(r);double term1 = b / 3.0;if (disc > 0) {/* 运用卡尔丹公式求得一个实根 */double s = r + sqrtf(disc);s = (s < 0) ? - FCPCubicRoot(-s) : FCPCubicRoot(s);double t = r - sqrtf(disc);t = (t < 0) ? - FCPCubicRoot(-t) : FCPCubicRoot(t);double result = -term1 + s + t;if (result >= 0 && result <= 1) return result;} else if (disc == 0) {double r13 = (r < 0) ? - FCPCubicRoot(-r) : FCPCubicRoot(r);double result = -term1 + 2.0 * r13;if (result >= 0 && result <= 1) return result;result = -(r13 + term1);if (result >= 0 && result <= 1) return result;} else {q = -q;double dum1 = q * q * q;dum1 = acosf(r / sqrtf(dum1));double r13 = 2.0 * sqrtf(q);double result = -term1 + r13 * cos(dum1 / 3.0);if (result >= 0 && result <= 1) return result;result = -term1 + r13 * cos((dum1 + 2.0 * M_PI) / 3.0);if (result >= 0 && result <= 1) return result;result = -term1 + r13 * cos((dum1 + 4.0 * M_PI) / 3.0);if (result >= 0 && result <= 1) return result;}return -1;
}

上面代码中用到的知识:

  • 一元三次方程判别式:

△=q24+p227△ = \frac{q^2}{4} + \frac{p^2}{27}△=4q2​+27p2​

  • 标准型方程中卡尔丹公式的一个实根:

初识贝塞尔(bezier)曲线相关推荐

  1. Unity 工具类 之 贝塞尔 Bezier 曲线

    Unity 工具类 之 贝塞尔 Bezier 曲线 目录 Unity 工具类 之 贝塞尔 Bezier 曲线 一.简单介绍 二.原理与分类 三.公式与原理图演示 五.注意事项 六.样例使用步骤(三次贝 ...

  2. 贝塞尔Bezier曲线的使用

    1 简介   贝塞尔曲线就是这样的一条曲线,它是依据N个位置任意的点坐标绘制出的一条光滑曲线.那么,我们可以直观地认为,为了得到一条贝塞尔曲线,我们只要输入起点.终点及控制点既可.变化参数t都是位于[ ...

  3. C#绘制带控制点的Bezier曲线,用于点阵图像及矢量图形

    [摘要]不借助第三方, 使用c# + GDI+进行SVG等绘图,绘制带控制点的Bezier曲线.可用于点阵图像及矢量图形(如SVG)绘图.先看效果: (不知为何,已两次上传图片,无法显示,求助csdn ...

  4. Bezier(贝塞尔)曲线小总结

    在初学时,我发现Bezier曲线(中文名贝塞尔曲线,想要了解历史发展等的可以看此百度百科:贝塞尔曲线_百度百科)很难理解,故在此写了一篇自己的心得感悟.要理解它最重要的是理解Bernstein基函数. ...

  5. matlab 贝塞尔曲线,基于MATLAB动态实现Bezier曲线几何作图.pdf

    基于MATLAB动态实现Bezier曲线几何作图.pdf 2015年 1月 黑龙江生态工程职业学院学报 Jan.2O15 第28卷第 1期 JournalofHeilongjiangVocationa ...

  6. 曲线数学NURBS之bezier曲线

    最近新研究topic是NURBS,NURBS(Non Uniform Rational B-spline)即非均匀有理B样条曲线.往往提到B样条.以及NURBS就会提到bezier曲线,他们之间的关系 ...

  7. 样条之贝塞尔(Bezier)

    我曾经发过两篇关于贝塞尔的文章:数学图形(1.47)贝塞尔(Bézier)曲线,数学图形之贝塞尔(Bézier)曲面.那是使用我自己定义的脚本语言生成贝塞尔图形.由于我自己定义的脚本语法功能有限,所以 ...

  8. 开源项目推荐:Bezier曲线、B-Spline和NURBS的区别与《THE NURBS BOOK 2nd》简介,曲线拟合可视化工具

    一.基本概念 B-Spline:B样条曲线 NURBS(Non Uniform Rational B-Spline):非均匀有理B样条曲线 B样条曲线有三种类型: 当起始点和终止点的重复度为最高次数加 ...

  9. 【四足机器人--摆动相足端位置速度轨迹规划】(4.1)FootSwingTrajectory(bezier曲线计算脚的摆动轨迹)代码解析

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 一.FootSwingTrajectory(bezier曲线)的内容 ...

  10. 轨迹规划——Bezier曲线与B样条曲线

    一.Bezier曲线 1.Bezier曲线的背景 给定n+1个数据点,p0~pn,生成一条曲线,使得该曲线与这些点描述的形状相符. (如果要求曲线通过所有数据点,则属于插值问题:如果只要求曲线逼近这些 ...

最新文章

  1. Paddle网络结构中的层和模型
  2. matlab画图横纵轴刻度相关设置
  3. mysql面试常用命令_面试之MySQL基本命令
  4. 世界范围内糖化血红蛋白报告的3种建议形式
  5. linux在什么环境运行,Linux环境变量是什么
  6. 鸿蒙OS应用开发_基础篇_编写第一个HarmonyOs应用_体会HarmonyOs的一次开发多端部署_以及分布式任务调度_IDE安装_了解应用组件以及应用布局---HarmonyOs开发工作笔记001
  7. shell编程学习笔记之特殊变量($0、$1、$2、 $?、 $# 、$@、 $*)
  8. HttpClient 4.5.3 模拟登陆CSDN
  9. 方便好用的论文管理软件EndNote X9 + PDF阅读编辑器Adobe Acrobat DC(2)
  10. 实现计算机考试和vb交换的,2012江苏省计算机二级VB试题库及答案
  11. 如何绘制逻辑图 — 8.逻辑的表达:数据逻辑
  12. C语言 汉字名字排列组合
  13. 生物信息学入门之基本概念之蛋白质同源检测和折叠识别
  14. 计算机计算资产分析表,财务指标计算器.xls
  15. 物联网产品的发展简介(一)【产品篇01】
  16. web网页本地视频播放器
  17. win7搜索文件 服务器,win7系统搜索不到文件的解决方法
  18. android设备连接win10,win10手机连接Android设备、iphone 操作方法
  19. LessLyrics 苹果Mac歌词软件 iTunes歌词助手
  20. 云服务器和云虚拟主机有什么区别

热门文章

  1. Visual Studio Code中对某变量名批量修改
  2. How to add SMSC feature into Android
  3. dart 语言是jvm_为什么发明基于 Dart 语言的 flutter 作为跨平台开发?
  4. arch检验python_Python玩转金融时间序列之ARCH与GARCH模型
  5. 165体重_我身高165cm标准体重是多少斤
  6. O2OA二次开发办公平台:内容管理数据迁移
  7. 【Matplotlib】在Jupyter交互页面中绘制折线图对比(自用函数)
  8. Internet Explorer 11: “请不要再叫我 IE”
  9. 这发文助手智力有点低下啊
  10. php array_diff菜鸟,TCGA