初识贝塞尔(bezier)曲线

文章目录

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


资料援引

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

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

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


贝塞尔曲线的用途

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

在这里插入图片描述

在这里插入图片描述


一阶贝塞尔(bezier)曲线

在这里插入图片描述

如上,P0P_0P0P1P_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(P0P1t)=(1t)P0+tP1

一阶贝塞尔曲线有两个端点P0P_0P0P1P_1P1 ),0个控制点


二阶贝塞尔(bezier)曲线

在这里插入图片描述

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

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

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

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

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

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

  • 二阶贝塞尔曲线公式:

P=(1−t)Q1+tQ2P = (1-t)Q_1 + tQ_2P=(1t)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)=(1t)((1t)P0+tP1)+t((1t)P1+tP2)
=(1−t)2P0+2t(t−1)P1+t2P2=(1-t)^2P_0+2t(t-1)P_1+t^2P_2=(1t)2P0+2t(t1)P1+t2P2

二阶贝塞尔曲线有两个端点P0P_0P0P2P_2P2),一个控制点P1P_1P1)。


三阶贝塞尔(bezier)曲线

在这里插入图片描述

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

  • P0P_0P0P1P_1P1P2P_2P2P3P_3P3 通过生成插值点 Q1Q_1Q1Q2Q_2Q2Q3Q_3Q3 来构成二阶贝塞尔(绿色线段)
  • 在此基础上生成插值点 O1O_1O1O2O_2O2 来构成一阶贝塞尔(蓝色线段)
  • 之后以 O1O_1O1O2O_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=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3

三阶贝塞尔曲线有两个端点P0P_0P0P3P_3P3),两个控制点P1P_1P1P2P_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=0nPiBi,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)=Cniti(1t)ni=i!(ni)!n!ti(1t)ni,【i=0,1,...,n


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

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

已知两个端点和两个控制点的情况下,根据 动画进度向量 PxP_xPxttt,再由 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=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3

公式中的 P0P_0P0P1P_1P1 等都是二维向量,由两个一维向量 PxP_xPxPyP_yPy 构成。而我们根据 tttPPP,本质上是根据 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=(1t)3Py0+3t(1t)2Py1+3t2(1t)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=(1t)3Px0+3t(1t)2Px1+3t2(1t)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(1t)2Py1+3t2(1t)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=3Py1t6Py1t2+3Py1t3+3Py2t23Py2t3+t3

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

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

提取一次方系数 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+ctx=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=(1t)3Py0+3t(1t)2Py1+3t2(1t)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=0x 的形式传值的,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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python代码测试 vim_用 Hypothesis 快速测试你的 Python 代码

点击上方“Python编程时光”&#xff0c;选择“加为星标”第一时间关注Python技术干货&#xff01;介绍无论你使用哪种编程语言或框架&#xff0c;测试都非常重要。Hypothesis是 Python 的一个高级测试库。它允许编写测试用例时参数化&#xff0c;然后生成使测试失败的简单易懂…

Mac 下 CMake 的配置与使用

文章目录安装与配置编译单个源文件编译前的准备开始编译编译多个源文件多个源文件在同一目录下多个源文件在不同目录下math 目录下的 CMakeLists.txt根目录的 CMakeLists.txtoption 选项导入外部库本地导入&#xff08;find_package&#xff09;外部导入&#xff08;FetchConte…

五轴编程_沙井万丰数控数控编程五轴编程那个软件好用

沙井万丰数控数控编程五轴编程那个软件好用设计需要掌握很高很全面的知识和技能&#xff0c;模具做的好&#xff0c;产品质量好&#xff0c;模具结构合理&#xff0c;生产效率高&#xff0c;工厂效益好。正因如此&#xff0c;模具技术工在外打工的工资都非常的高。少则每月几千…

Linux学习:第二章-Linux安装

一虚拟机使用 VMware主要特点&#xff1a; 1、不需要分区或重新开机就能在同一台PC上使用两种以上的操作系统 2、本机系统可以与虚拟机系统网络通信 3、可以设定并且随时修改虚拟机操作系统的硬件环境 二安装方式 图形安装&#xff1a;直接回车 字符安装&#xff1a;linux tex…

keil3如何放大字体_国潮海报不会做?送你国风字体+图案笔刷+PSD素材+包装样机...

有很多朋友都问带鱼&#xff0c;国潮风的海报到底应该怎么做呢&#xff1f;首先你要知道什么是国潮风&#xff1a;国潮风就是现代文化和古代文化的碰撞&#xff0c;是年轻人的态度&#xff01;那么应该如何构图如何设计呢&#xff1f;如何配色如何搭配字体呢&#xff1f;这些方…

Google 开源项目风格指南学习笔记——C++篇

文章目录前言0. 缩写名词解释1. 头文件1.1. Self-contained 头文件1.2. 头文件保护1.3. 前置声明1.4 内联函数1.5. #include 的路径及顺序2. 作用域2.1. 命名空间2.2. 非成员函数、静态成员函数和全局函数2.3. 局部变量2.4. 静态和全局变量3. 类3.1. 构造函数的职责3.2. 隐式类…

hiveserver2启动不起来_给爱车配个充电宝,70迈汽车应急启动电源,让你远离搭电小广告...

说到汽车应急启动其实我有切身的痛&#xff0c;在哈尔滨零下35的严冬&#xff0c;晚上带着女神吃完饭&#xff0c;高高兴兴地吃完以后一上车&#xff0c;发现电瓶被冻没电了&#xff0c;天知道当时有多尴尬。马上叫了保险公司过来给搭电&#xff0c;结果在饭店从晚上8点一直等到…

Windows 下 VS 配置 OpenGL 环境

文章目录前言获取 GLFW打开 VS前言 感谢B站同学搬运YouTube上的教学视频。 获取 GLFW 从官网上下载GLFW macOS下64位二进制文件 打开 VS 新建解决方案 OpenGL test&#xff0c;并在解决方案中新建文件夹 Dependencies&#xff1a; 从下载好的 glfw 文件夹中找到最新版链接…

ubuntu 网卡双网口 配置_无线网卡m2 ngff keya keye、minipcie接口改转多口有线网卡实现软路...

小型主板及笔记本中的无线网卡m2ngffkeyakeye接口&#xff08;CNVI除外&#xff09;通过m2ngffkeyae转接pcie1x转接板&#xff0c;或者无线网卡的minipcie接口&#xff0c;通过minipcie转接pcie1x转接板可以改装有线网卡板卡&#xff0c;来实现软路由功能。m2ngffkeyae转接pcie…

OpenGL | 通过绘制一个三角形来入门 OpenGL 图形渲染管线

文章目录前言什么是 OpenGl &#xff1f;回顾openGL 的 Object显存结构工作阶段通过顶点缓冲对象将顶点数据初始化至缓冲中标准化设备坐标顶点缓冲对象 VBOglGenBuffersglBindBufferglBufferData建立了一个顶点和一个片段着色器着色器是什么&#xff1f;为什么需要使用着色器&a…

javascript特效_如何在网页添加鼠标点击特效

经常有同学问我怎么做到的&#xff0c;本论坛属于DZ当然用的是插件啦。偶然在网上找到一个关于wordpress的特效代码&#xff0c;分享给大家。WordPress 添加鼠标点击特效实际上这种教程在网上搜索一下有一大堆&#xff0c;已经是各大博主玩烂的东西了。不过既然给自己的博客加上…

Android |双锁单例模式中使用Context如何避免内存泄露的 Warning 提示

文章目录问题解决方法拓展问题 在Android开发中&#xff0c;经常会将工具类以单例模式的方法实现&#xff0c;而工具类中又总不可避免的用到 Context &#xff0c;例如&#xff1a; public class MySingleton {private static volatile MySingleton instance;private final C…

android nio debug模式正常 release包crash_Flutter包大小治理上的探索与实践

Flutter作为一种全新的响应式、跨平台、高性能的移动开发框架&#xff0c;在性能、稳定性和多端体验一致上都有着较好的表现&#xff0c;自开源以来&#xff0c;已经受到越来越多开发者的喜爱。但是&#xff0c;Flutter的引入往往带来包体积的增大&#xff0c;给很多研发团队带…

Linux学习:第三章-Linux常用命令-1

提示符&#xff1a; [rootlocalhost ~]# [登录用户名主机名 当前所在目录]提示符 ~家目录 /root /home/aa 提示符&#xff1a; #超级用户 $普通用户 一 linux命令的格式1、命令 [选项] [参数]lslist显示目录下内容 ①命令名称&#xff1a;ls 命令英文原意&#xff1a;list 命令…

Leetcode每日一题:使括号有效的最少添加(括号匹配)

文章目录题目解析贪心趣解题目 只有满足下面几点之一&#xff0c;括号字符串才是有效的&#xff1a; 它是一个空字符串&#xff0c;或者它可以被写成 AB &#xff08;A 与 B 连接&#xff09;, 其中 A 和 B 都是有效字符串&#xff0c;或者它可以被写作 (A)&#xff0c;其中 …

yolov5搭建环境_Yolov5环境配置和训练私有数据,YOLOv5,以及,私人

1.使用anaconda安装python3.8的环境conda create -n yolo5 python3.8#anaconda下载地址: https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/2.安装pytorch1.6 torchvision0.7conda install pytorch1.6 torchvision0.7 cudatoolkit10.13.安装相关包pip install opencv-py…

sam格式的结构和意义_各种格式的练字本,对写字真有帮助吗

图片来源于笔势通各种格式的练字本现在越来越多&#xff0c;目的主要是便于学生把握好笔画的位置和布局&#xff0c;从而把整个字的结构处理好&#xff0c;常见的有米字格&#xff0c;回宫格等。这些练字本对于初学者来说肯定是有帮助的&#xff0c;特别是低年级学生。当然随着…

硬件结构图_那曲地表水电子除垢仪结构图

那曲地表水电子除垢仪结构图水处理设备也应断电停止使用&#xff0c;系统长期停止运行或季节性停止运行&#xff0c;在系统停止运行前&#xff0c;在水中投加适量缓蚀剂&#xff0c;并采取满水湿保护的措施&#xff0c;以减小腐蚀&#xff0c;保护系统。开启进水阀检查无误后电…

dtm文件生成等高线 lisp_南方cass如何用图面高程点生成等高线

展开全部1、首先点击cass菜单栏中的等高线菜单下的建立DTM&#xff0c;弹出对话框。可以有两种方式建32313133353236313431303231363533e58685e5aeb931333431356665立DTM&#xff0c;由数据文件生成或者由图面高程点生成&#xff0c;第一种直接在对话框中选择相应的数据文件&am…

postgresql兴建用户_PostgreSQL 12.2, 11.7, 10.12, 9.6.17, 9.5.21, 和 9.

### **译者&#xff1a;朱君鹏**### **发表于2020年2月13日 作者&#xff1a;PostgreSQL全球开发小组**#### 全球开发小组已发布的更新涵盖所有支持的版本&#xff0c;包括12.2&#xff0c;11.7&#xff0c;10.12&#xff0c;9.6.17&#xff0c;9.5.21和9.4.26。该版本修正了一…