【转】从零开始学图形学:10分钟看懂贝塞尔曲线

转自:https://zhuanlan.zhihu.com/p/344934774

从零开始学图形学:10分钟看懂贝塞尔曲线

引入

在画画的时候,你可能会遇到画曲线的情况。比如你想画一个肥宅的大肚子轮廓,此时你随手一画,发现不好看,感觉太鼓了,于是你只能重新画,再画一遍,发现太小了,于是只能再重新画,如此反复许多次之后,你终于画对了。

作为一个天才小画家,你心里想,如果有一个小滑块,可以在保证曲线平滑的情况下,通过拉动滑块实现曲线形状的调节,那不就不用来回画了吗!

嘿,您别说,还真有,这个东西就叫做贝塞尔曲线(Bézier curve),有了这个,你便可以像这样调节曲线:

是不是很熟悉?没错!贝塞尔曲线广泛应用于各种绘图相关的软件中,甚至计算机中的字体设计就全靠贝塞尔曲线来控制。

接下来,我们详细讲一讲贝塞尔曲线的原理。

一个简单的例子

讲之前,我们先看一张图:

这里的 P0、P1、P2 分别称之为控制点,贝塞尔曲线的产生完全与这三个点位置相关。

这也就意味着,我们可以通过调节控制点的位置,进而调整整个曲线。

贝塞尔曲线是一个对强迫症极其友好的曲线,看这个动图就让人很舒适,而它的构造方法也一样让人很舒适。

最开始,对于绿色线段的两头 Q0 和 Q1,将其分别放在 P0 和 P1 的位置,此时让它们运动,要求:Q0 往 P1 方向,Q1 往 P2 方向,分别匀速运动,并且同时到达线段的另一头。

转化成数学公式,即为

 

在绿色线段上再取一个点 B ,如果 B 在绿色线段上的运动也满足上述的规律,那岂不是很爽!所以不妨再规定:

 

令上述等式等于 t,t 肯定是 [0,1] 的,其意义是点在它所处线段的位置。那么随着 t 的增大,Q0、Q1、B 的位置也就随之确定了!最终 B 的轨迹,便构成了贝塞尔曲线。

递归性质

仔细观察一下上述的构造过程,我们可以观察到:

首先,有三个控制点;

三个控制点形成两个线段,每个线段上有一个点在运动,于是得到两个点;

两个点形成一个线段,这个线段上有一个点在运动,于是得到一个点;

最后一个点的运动轨迹便构成了贝塞尔曲线!

我们发现,实际上是每轮都是 n 个点,形成 n-1 条线段,每个线段上有一个点在运动,那么就只关注这 n-1 个点,循环往复。最终只剩一个点时,它的轨迹便是结果。

那么,似乎最开始的控制点,也不一定是三个?如果是四个、五个,甚至更多呢?

当然有——

如此一来,你会发现贝塞尔曲线内的递归结构。实际上,上述介绍的分别是三阶、四阶、五阶的贝塞尔曲线,贝塞尔曲线可以由阶数递归定义。

公式

到了最讨厌的公式环节。

点在线段上,可以用  来表示。如果你把整个递归的每一步都按这个展开,那么最终可以得到如下公式:

 

分段贝塞尔曲线

虽然贝塞尔曲线的阶数可以很高,但是如果曲线的阶数过高,调整控制点对曲线的影响就比较小,调整起来相当麻烦。

 

于是,我们常常使用分段的贝塞尔曲线,保证每一小段不会太复杂。这样每次只用调小段,还可以做到只调局部不影响大局,那就相当舒服了。

分段带来的唯一问题是,曲线在段与段的交界处,如何保证平滑?

所谓平滑,其实就是一阶导数连续,也就是左右导数的极限相同。

对两侧的贝塞尔曲线求导,分别代入 t=0 和 t=1 (即贝塞尔曲线的开始和结束时间),让二者相等。此时能发现,当两侧控制点与分段交接点共线且形成的线段长度相等时,满足曲线平滑性质。

 

再分享一个网站,可以在线玩贝塞尔曲线:链接

实验

内容:

  1. 完成贝塞尔曲线的递归定义函数。
  2. 对曲线进行反走样(Anti-Aliasing, AA)。

解析

简单的不得了,直接带入点在线段上的公式即可。

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{// TODO: Implement de Casteljau's algorithmif(control_points.size() == 1) return control_points[0];std::vector<cv::Point2f> a;for(int i = 0;i+1 < control_points.size();i ++) {auto p = control_points[i] + t * (control_points[i+1] - control_points[i]);a.push_back(p);}return recursive_bezier(a, t);
}

对于 AA ,只需要在曲线附近做点插值就行,满足离曲线越近的像素的像素值越高,越远的越低,即可。这里随便写了点:

  1. For 2*2 grids,  for each  .
void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window) 
{// TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's // recursive Bezier algorithm.double delta = 0.001;for(double t = 0;t <= 1;t += delta) {auto point = recursive_bezier(control_points, t);int w = 1;for(int i = -w+1;i <= w;i ++) {for(int j = -w+1;j <= w;j ++) {int x = point.x + i, y = point.y + j;double dist = sqrt(pow(point.x-x,2) + pow(point.y-y,2));window.at<cv::Vec3b>(y, x)[1] = std::min(window.at<cv::Vec3b>(y, x)[1] + 255 * std::max(2-exp(dist),0.0), 255.0);// auto k = abs(((int)(point.x+1-i))-point.x) * abs(((int)(point.y+1-j))-point.y);// window.at<cv::Vec3b>(y, x)[1] = std::min(window.at<cv::Vec3b>(y, x)[1] + 255 * k, 255.0f);}}}}

注:图源网络、games101课件

 

本文首发于知乎专栏图形图像与机器学习,以后会常更新,欢迎大家的关注与催更。

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

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

相关文章

Linux重启网卡的方法

重启网卡的几种方法&#xff1a;一、network利用root帐户# service network restart二、ifdown/ifup# ifdown eth0# ifup eth0三、ifconfig# ifconfig eth0 down# ifconfig eth0 up

android webview url scheme,Android Webview ERR_UNKNOWN_URL_SCHEME错误

当我点击链接到mailto&#xff1a;adminikiyuzoniki.net时,我收到此错误&#xff1a;net: ERR_UNKNOWN_URL_SCHEME我试图添加一个if(url.startsWith(“mailto&#xff1a;”))条件,但它不起作用.这是我的MyWebViewClient方法&#xff1a;public class MyWebViewClient extends …

【转】编译DCMTK

转自&#xff1a;https://blog.51cto.com/u_2845385/1053291 也许是DCMTK的开发人员认为DCMTK是一个专用的库&#xff0c;没有必要做成动态链接库。 也许是DCMTK的开发人员认为DCMTK需要跨平台&#xff0c;做成动态链接库就平台相关了&#xff0c;违背了跨平台的本意。 …

WinCE6下的kernelIoControl使用方法

WinCE6下的kernelIoControl可不是谁都可以调的。 这个可能很多人曾经知道&#xff0c;但是老是忘记&#xff0c;比如我&#xff0c;哈哈。 kerneliocontrol以前在CE5下面耍惯了&#xff0c;用这个接口&#xff0c;AP简直可以无法无天啊&#xff0c;动不动就reset你&#xff0…

Flex 学习随笔 ---- 使用WebService 与数据库连接

任何一个网络工具&#xff0c; 如果不能和数据打交道&#xff0c;那它就是失败的。 还好Flex是可以的&#xff0c;由于本人刚学&#xff0c;就用asp.netc#来讲下这个简单的连接。 Flex 和数据库通讯现在只能使用Service&#xff0c;如httpservice,rpcservice,webservice等等。 …

第三方登录android代码,Android Learning:微信第三方登录(示例代码)

这两天&#xff0c;解决了微信第三方授权登录的问题&#xff0c;作为一个新手&#xff0c;想想也是一把辛酸泪。我想着&#xff0c;就把我的遇到的坑给大家分享一下&#xff0c;避免新手遇到我这样的问题能够顺利避开。步骤一 微信开发者平台我开始的解决思路是&#xff0c;去这…

【转】基于DCMTK的DICOM相关程序编写攻略

转自&#xff1a;https://blog.csdn.net/jackmacro/article/details/5332654 零、前言&#xff1a; 由于现在的医学影像设备的图像存储和传输正在逐渐向DICOM标准靠拢&#xff0c;因此&#xff0c;我们在进行医学图像处理的过程中&#xff0c;经常需要自己编写和DICOM格式的图…

对象与函数

摘自《UMLOOPC嵌入式C语言开发精讲》 11.3 对象与函数11.3.1 函数的角色经济诺贝尔奖得主H.A.Simon&#xff08;H.A.Simon&#xff0c;计算机人工智能之父&#xff09;在其1962年的文章《The Architecture of Complexity》中说道&#xff1a;“从小系统建造成庞大系统时&…

android打包工具多渠道批量打包,Android 快速渠道批量打包详解教程-美团多渠道打包方案...

今天写一篇文章来总结下android批量打渠道包美团版本。之前项目上一直用的是gradle 批量打包方式&#xff0c;那个速度啊真是令人发指&#xff0c;15个渠道得跑上半个小时&#xff0c;出去吃顿饭回来&#xff0c;还在跑。特别是赶上项目上线的话&#xff0c;如果给测试提交了正…

【转】云社区 博客 博客详情 二维异形件排版算法介绍(一)

转自&#xff1a;https://bbs.huaweicloud.com/blogs/175385 【摘要】 排样问题&#xff08;Nesting Problem&#xff09;又称为下料问题(Cutting and stock problems)或填充问题(Packing Problem)&#xff0c;其目标是在材料切割过程中寻找一个较高的材料利用率。排样问题属于…

增加RIL组件时编辑出现的问题

当我把RIL组件加进到CE6的项目里去编译,本来好好的项目编译不过去了,我就选择了RIL的所有feature&#xff0c;没有改任何东西,理论上sysgen就可以了&#xff0c;但是确总是编译不过去 看根目录下的build.log: E:/WINCE600/OSDesigns/POS410/POS410/Wince600/E6000_ARMV4I/cesy…

android 微信两个服务的,微信上线两个新功能

微信又上线新功能了&#xff01;一个与你的钱包相关&#xff0c;一个与你的微信号相关&#xff0c;赶紧来看看吧微信支付分全面开放每个微信用户都拥有自己的一个分数值 &#xff0c;且每月根据综合数据更新一次。达到一定的分值门槛&#xff0c;用户即可享受超千项便捷服务。这…

【转】二维异形件排版算法介绍(二)

转自&#xff1a;https://bbs.huaweicloud.com/blogs/196289【摘要】 二维不规则异形件主要有两种策略&#xff1a;分别是基于可行解的排样策略和基于重叠移除的排样策略。所谓基于可行解的排样策略&#xff0c;是指在排样过程中零件之间始终是不重叠的&#xff0c;而基于重叠移…

解决用户控件循环引用的笨办法

在上一篇中提到使用MagicAjax开发了一个项目,因为做这个项目之前对MagicAjax不是很了解,在里面使用了用户控件,我发现这是一个很致命的错误,用户控件在MagicAjax中用起来很痛苦.因我们的项目不是简单的只要求"看起来像"没有刷新页面,其中一个主要的页面有那么一点点复…

【转】介绍一些免费/开源的医学影像后处理工具

转自&#xff1a;https://blog.csdn.net/liaopiankun0618/article/details/84328331 来源&#xff1a;融视影像科技 综述 医学影像的处理有两个特质。一是复杂&#xff0c;整个处理流程涉及多种算法&#xff0c;需要调整的参数较多。二是发展快&#xff0c;从采集、重建到后…

android 安卓市场,安卓市场(Android Market).doc

安卓市场(Android Market)安卓市场(Android Market)Android (HiMarket) China market is the earliest and largest Android software and online game download platform, Netdragon well-known products, provide good service for users of mobile phone software. Since it…

IROM 浅见

IROM其实是三星已经固化在CPU里面的一段代码&#xff0c;我们要做的事情就是提供一段可以供IROM读取的8K代码程序&#xff0c;至于这8K代码放在哪里&#xff0c;由CPU的相关引脚决定&#xff08;OM1-OM4&#xff0c;GPN13-GPN15&#xff09;。也就是说从哪里启动的意思。 如果从…

LINQ是死是活?——很奇怪为什么会有这样的话题?

LINQ是死是活?&#xff1f;&#xff1f; 如果我问您&#xff1a;机器代码死了吗&#xff1f;汇编死了吗&#xff1f;C死了吗&#xff1f;VB6.0死了吗&#xff1f;Delphi死了吗&#xff1f;您可能会觉得我很无聊。所以我觉得“LINQ是死是活”的说法也很奇怪&#xff1f; LINQ诞…

android gradle 语法,Gradle 1.语法

8种机械键盘轴体对比本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f;gradle 语法解析:gradle的语法十分简洁,以至于看起来跟像是配置文件。先看一个简单的android项目完整的gradle脚本&#xff1a;build.gradle123456789101112131415…

【转】二维异形件排版算法介绍(三)

转自&#xff1a;https://bbs.huaweicloud.com/blogs/203947 【摘要】 相比于基于可行解的排样算法&#xff0c;重叠移除算法在改变解的状态时&#xff0c;允许零件之间发生重叠&#xff0c;然后采用分离技术消除重叠&#xff0c;直到达到算法的终止条件为止。重叠移除算法的关…