文章目录

  • 一、前言
  • 二、最终效果
    • 1、Unity演示效果
    • 2、Unity Demo源码工程
  • 三、贝塞尔曲线原理
    • 1、什么是阶(次)
    • 2、一阶贝塞尔曲线(一元一次函数,线性函数)
    • 3、二阶贝塞尔曲线(一元二次函数)
    • 4、三阶贝塞尔曲线
    • 5、测试50阶贝塞尔曲线
  • 四、贝塞尔曲线本地实验
    • 1、工程地址
    • 2、运行
  • 五、 贝塞尔曲线在线玩
    • 1、贝塞尔曲线路径运动模拟
    • 2、贝塞尔曲线画线练习
    • 3、三次贝塞尔曲线缓动演示(css)
    • 4、HTML5贝塞尔曲线代码生成器(canvas)
  • 六、Unity实现贝塞尔曲线
    • 1、贝塞尔曲线C#代码:BezierCurve.cs
    • 2、使用LineRenderer绘制曲线
    • 3、制作贝塞尔曲线预设
    • 4、更新LineRenderer点坐标:LineRendererCtrler.cs
    • 5、鼠标控制控制点:PointHandle.cs
    • 6、UI交互:UICtrler.cs
    • 7、运行测试
  • 七、结束语

一、前言

点关注不迷路,持续输出Unity干货文章。
嗨,大家好,我是新发。
有同学私信我,问我能否出一个三阶贝塞尔曲线的教程。

嘛,今天就来讲讲贝塞尔曲线吧。

二、最终效果

1、Unity演示效果

最终运行效果如下:

2、Unity Demo源码工程

本文Demo工程已上传到CodeChina,感兴趣的同学可自行下载学习。
地址:https://codechina.csdn.net/linxinfa/unitybeziercurvedrawdemo
注:我使用的Unity版本:Unity 2020.1.14f1c1 (64-bit)

三、贝塞尔曲线原理

贝塞尔曲线(Bezier curve),又称 贝兹曲线贝济埃曲线 ,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,当时主要用于汽车主体设计。现主要应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,比如PhotoShop中的钢笔工具。

PhotoShop中的钢笔工具是一个三阶的贝塞尔曲线。

1、什么是阶(次)

我们说的三阶贝塞尔曲线,三阶是什么意思?
两种理解:
1、贝塞尔曲线,它的背后是一个数学函数,N阶可以理解为N次方的意思,我们也可以把三阶贝塞尔曲线叫做三次贝塞尔曲线。
2、二阶贝塞尔曲线就是在一阶贝塞尔曲线的基础上再求一次一阶贝塞尔曲线;三阶贝塞尔曲线就是在二阶贝塞尔曲线的基础上再求一次一阶贝塞尔曲线;以此类推。

我觉得第二种理解更准确一点。

2、一阶贝塞尔曲线(一元一次函数,线性函数)

我们先来看看 一阶贝塞尔曲线 的公式:
给定点P1 P2,函数推导式如下:

一阶贝塞尔曲线公式:B(t) = P1 + (P2 − P1)t = P1(1−t)+ P2t, t∈[0,1]

动态效果如下:

注:应该会有同学想问下图这个动态效果是用什么软件做的,是使用js写的,感兴趣的同学可以跳到文章第四节:贝塞尔曲线本地实验

从动态效果看,我们可以看出,一阶贝塞尔曲线其实就是一条线性的线段。

3、二阶贝塞尔曲线(一元二次函数)

二阶贝塞尔曲线的路径由给定点P1 、P2、P3 的函数B(t)给出:

二阶贝塞尔曲线公式:B(t) = P1(1 - t)2 + 2P2t(1 - t) + P3t2,t∈[0,1]

动态效果如下:

我们可以拆解一下这个过程,从P1到P2是一个一阶贝塞尔曲线过程,从P2到P3也是一个一阶贝塞尔曲线过程,这两个过程同时进行,得到一条新的线段MN。

在MN上再同时进行一阶贝塞尔曲线过程,这样得到的,就是二阶贝塞尔曲线了。
这样,我们可以把二阶贝塞尔曲线的公式拆解一下:

M(t) = P1(1 - t) + P2t
N(t) = P2(1 - t) + P3t
B(t) = M(1 - t) + Nt

我们将M和N带入B(t)函数中,得到的就是:

B(t) = (1 - t)( (1 - t)P1 + tP2) + t((1 - t)P2 + tP3)

化简一下,就是:

B(t) = P1 - 2P1t + P1t2 + 2P2t - 2P1t2 + P3t3

再整理一下,就是:

B(t) = P1(1 - t)2 + 2P2t(1 - t) + P3t2

这正是我们上面一开始列的二阶贝塞尔曲线的公式。

4、三阶贝塞尔曲线

根据一阶、二阶的贝塞尔曲线的原理,相信大家已经知道三阶贝塞尔曲线的推导了吧。

三阶贝塞尔曲线公式:B(t) = P1(1 - t)3 + 3P2t(1 - t)2 + 3P3t2(1 - t) + P4t3,t∈[0,1]

动态效果如下:

写到这里了,我就顺便推导一下好了,先标一下点,如下:

我们先对X、Y、Z三点分别做一阶贝塞尔,得到:

X(t) = P1(1 - t) + P2t
Y(t) = P2(1 - t) + P3t
Z(t) = P3(1 - t) + P4t

接着我们对M、N两点分别做一阶贝塞尔,得到:

M(t) = X(1 - t) + Yt
N(t) = Y(1 - t) + Zt

带入X、Y、Z,得到:

M(t) = P1(1 - t)2 + 2P2t(1 - t) + P3t2
N(t) = P2(1 - t)2 + 2P3t(1 - t) + P4t2

到这里就可以看出,其实M(t)和N(t)就是二阶贝塞尔,三阶贝塞尔就是在二阶贝塞尔的基础上再求一次一阶贝塞尔。

对B点做一阶贝塞尔:

B(t) = M(1 - t) + Nt

带入M和N,得到公式:

B(t) = (P1(1 - t)2 + 2P2t(1 - t) + P3t2)(1 - t) + (P2(1 - t)2 + 2P3t(1 - t) + P4t2)t

化简并整理后,最终公式:

B(t) = P1(1 - t)3 + 3P2t(1 - t)2 + 3P3t2(1 - t) + P4t3,t∈[0,1]

5、测试50阶贝塞尔曲线

更高阶的贝塞尔曲线的公式推导就不一一写了,我们来玩个猛的,50阶贝塞尔曲线:

四、贝塞尔曲线本地实验

1、工程地址

上文中我演示的贝塞尔曲线动态效果,是通过GitHub的一个开源项目进行演示的。
GitHub源地址:https://github.com/Aaaaaaaty/bezierMaker.js

我在它的基础上做了一些改进,上传到了CodeChina,感兴趣的可以下载我的版本:
CodeChina地址:https://codechina.csdn.net/linxinfa/beziermaker

2、运行

下载下来后,直接用浏览器打开bezierMaker.html即可。

如下:

五、 贝塞尔曲线在线玩

感受到了数学之美了吗?感兴趣的同学可以自己玩一下。我发几个可以在线实验贝塞尔曲线的网址给大家吧。

1、贝塞尔曲线路径运动模拟

地址:https://csdjk.github.io/bezierPathCreater.github.io/

2、贝塞尔曲线画线练习

地址:https://bezier.method.ac/

3、三次贝塞尔曲线缓动演示(css)

地址:https://cubic-bezier.com/

4、HTML5贝塞尔曲线代码生成器(canvas)

地址:http://wx.karlew.com/canvas/bezier/

六、Unity实现贝塞尔曲线

1、贝塞尔曲线C#代码:BezierCurve.cs

上面啰嗦了这么多,我终于要讲Unity部分啦。上面的原理懂了之后,其实贝塞尔曲线的算法代码就不难了。
假设我们现在有个控制点的Transform数组。

Transform[] points;

那么,一阶贝塞尔曲线的算法就是:

public Vector3 lineBezier(float t)
{Vector3 a = points[0].position;Vector3 b = points[1].position;return a + (b - a) * t;
}

二阶贝塞尔曲线的算法:

// 二阶贝塞尔曲线
public Vector3 quardaticBezier(float t)
{Vector3 a = points[0].position;Vector3 b = points[1].position;Vector3 c = points[2].position;Vector3 aa = a + (b - a) * t;Vector3 bb = b + (c - b) * t;return aa + (bb - aa) * t;
}

三阶贝塞尔曲线的算法:

public Vector3 cubicBezier(float t)
{Vector3 a = points[0].position;Vector3 b = points[1].position;Vector3 c = points[2].position;Vector3 d = points[3].position;Vector3 aa = a + (b - a) * t;Vector3 bb = b + (c - b) * t;Vector3 cc = c + (d - c) * t;Vector3 aaa = aa + (bb - aa) * t;Vector3 bbb = bb + (cc - bb) * t;return aaa + (bbb - aaa) * t;
}

把算法封装BezierCurve脚本中。


BezierCurve.cs完整代码:

// BezierCurve.cs
using UnityEngine;/// <summary>
/// 贝塞尔曲线
/// </summary>
[ExecuteInEditMode]
public class BezierCurve : MonoBehaviour
{/// <summary>/// 控制点(包括起始点和终止点)/// </summary>[SerializeField]Transform[] points;/// <summary>/// 精确度/// </summary>[SerializeField]int accuracy = 20;void Update(){// 绘制贝塞尔曲线Vector3 prev_pos = points[0].position;for (int i = 0; i <= accuracy; ++i){Vector3 to = formula(i / (float)accuracy);Debug.DrawLine(prev_pos, to);prev_pos = to;}}void OnDrawGizmos(){Gizmos.color = Color.white;// 绘制控制点(包括起始点和终止点)for (int i = 0; i < points.Length; ++i){if (i < points.Length - 1){if (4 == points.Length && i == 1){continue;}Vector3 current = points[i].position;Vector3 next = points[i + 1].position;Gizmos.DrawLine(current, next);}}}/// <summary>/// 贝塞尔时间公式(二阶、三阶)/// </summary>/// <param name="t">时间参数,范围0~1</param>/// <returns></returns>public Vector3 formula(float t){switch(points.Length){case 3: return quardaticBezier(t);case 4: return cubicBezier(t);}return Vector3.zero;}/// <summary>/// 一阶贝塞尔/// </summary>/// <param name="t">时间参数,范围0~1</param>/// <returns></returns>public Vector3 lineBezier(float t){Vector3 a = points[0].position;Vector3 b = points[1].position;return a + (b - a) * t;}/// <summary>/// 二阶贝塞尔/// </summary>/// <param name="t">时间参数,范围0~1</param>/// <returns></returns>public Vector3 quardaticBezier(float t){Vector3 a = points[0].position;Vector3 b = points[1].position;Vector3 c = points[2].position;Vector3 aa = a + (b - a) * t;Vector3 bb = b + (c - b) * t;return aa + (bb - aa) * t;}/// <summary>/// 三阶贝塞尔/// </summary>/// <param name="t">时间参数,范围0~1</param>/// <returns></returns>public Vector3 cubicBezier(float t){Vector3 a = points[0].position;Vector3 b = points[1].position;Vector3 c = points[2].position;Vector3 d = points[3].position;Vector3 aa = a + (b - a) * t;Vector3 bb = b + (c - b) * t;Vector3 cc = c + (d - c) * t;Vector3 aaa = aa + (bb - aa) * t;Vector3 bbb = bb + (cc - bb) * t;return aaa + (bbb - aaa) * t;}
}

2、使用LineRenderer绘制曲线

曲线的绘制,我使用了LineRenderer组件,为了让效果看起来好看一点,我调了个彩虹渐变色,我把宽度调成两边细中间粗,这样看起来更加立体。

效果如下:

3、制作贝塞尔曲线预设

我分别制作了二阶贝塞尔曲线和三阶贝塞尔曲线的预设。
二阶贝塞尔曲线预设:


三阶贝塞尔曲线预设:

4、更新LineRenderer点坐标:LineRendererCtrler.cs

预设上挂的组件如下:

其中BezierCurve是贝塞尔曲线的算法逻辑,LineRendererCtrler是根据BezierCurve的算法实时更新LineRenderer组件的坐标点。
更新LineRenderer点坐标的接口如下:

// LineRenderer.cs// 设置LineRenderer点数量
public int positionCount { get; set; }
// 设置LineRenderer点坐标
public void SetPosition(int index, Vector3 position);

在预设上把控制点赋值给BezierCurve组件的points数组,三阶贝塞尔曲线有4个控制点,二阶贝塞尔曲线的话则是3个控制点。

LineRendererCtrler.cs完整代码:

// LineRendererCtrler.cs
using UnityEngine;/// <summary>
/// LineRenderer控制器
/// </summary>
[RequireComponent(typeof(LineRenderer))]
[RequireComponent(typeof(BezierCurve))]
public class LineRendererCtrler : MonoBehaviour
{[SerializeField]int nodeCount = 20;[SerializeField]LineRenderer lineRenderer;[SerializeField]BezierCurve bezier;void Awake(){lineRenderer.positionCount = nodeCount + 1;}void Update(){// 更新LineRenderer的点for (int i = 0; i <= nodeCount; ++i){Vector3 to = bezier.formula(i / (float)nodeCount);lineRenderer.SetPosition(i, to);}}
}

5、鼠标控制控制点:PointHandle.cs

控制点我用的是球体,我使用了射线检测来判断鼠标是否点中了控制点。
示例:

if (Input.GetMouseButtonDown(0))
{Ray ray = cam.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit, 100)){// 点中了控制点targetTrans = hit.transform;}
}

鼠标移动的时候,要把鼠标的屏幕坐标转换为世界坐标再更新控制点(球体)的坐标。
示例:

if (null != targetTrans && Input.GetMouseButton(0))
{var targetPos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, posZ));targetTrans.position = targetPos;
}

鼠标控制控制点的逻辑,我封装在PointHandle.cs脚本中。

控制点挂上PointHandle脚本。

PointHandle.cs完整代码:

using UnityEngine;/// <summary>
/// 控制点
/// </summary>
public class PointHandle : MonoBehaviour
{private Transform targetTrans;private Camera cam;private float posZ;private void Start(){cam = Camera.main;}private void Update(){// 鼠标左键按下if (Input.GetMouseButtonDown(0)){Ray ray = cam.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit, 100)){// 缓存射线碰撞到的物体targetTrans = hit.transform;// 缓存物体与摄像机的距离posZ = targetTrans.position.z - cam.transform.position.z;}}// 鼠标左键抬起if (Input.GetMouseButtonUp(0)){// 释放碰撞体缓存targetTrans = null;}// 鼠标按住中if (null != targetTrans && Input.GetMouseButton(0)){// 鼠标的屏幕坐标转成世界坐标// 由于鼠标的屏幕坐标的z轴是0,所以需要使用物体距离摄像机的距离为z周的值var targetPos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, posZ));targetTrans.position = targetPos;}}
}

6、UI交互:UICtrler.cs

简单搭建一下场景,制作一下界面


写个界面逻辑脚本UICtrler.cs

UICtrler.cs挂在Canvas节点上,并赋值对应成员。

UICtrler.cs代码如下:

// UICtrler.cs
using UnityEngine;
using UnityEngine.UI;public class UICtrler : MonoBehaviour
{/// <summary>/// 二阶贝塞尔曲线/// </summary>public GameObject bezierCurve2;/// <summary>/// 三阶贝塞尔曲线/// </summary>public GameObject bezierCurve3;public Toggle toggleBezier3;void Start(){toggleBezier3.onValueChanged.AddListener((v) => {bezierCurve3.SetActive(v);bezierCurve2.SetActive(!v);});// 默认显示三阶贝塞尔曲线bezierCurve3.SetActive(true);bezierCurve2.SetActive(false);}
}

7、运行测试

最终运行测试效果如下:

七、结束语

现在,贝塞尔曲线,你学会了吗?

喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~

【游戏开发进阶】玩转贝塞尔曲线,教你在Unity中画Bezier贝塞尔曲线(二阶、三阶),手把手教你推导公式相关推荐

  1. 【游戏开发进阶】带你玩转模型法线,实验一下大胆的想法(法线贴图 | shader | Unity | python | 爬虫)

    文章目录 一.前言 二.直观感受法线贴图 三.表面法线 1.表面法线的概念 2.空间与坐标系 2.1.世界空间--世界坐标系 2.2.局部空间--局部坐标系 2.3.切线空间--切线坐标系 2.4.小 ...

  2. HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!)

    HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!) [印]香卡(Shankar,A.R.) 著 谢光磊 译 ISBN 978-7-121-21226-0 ...

  3. dotween曲线运动 unity_【Unity3d游戏开发】游戏中的贝塞尔曲线以及其在Unity中的实现...

    RT,马三最近在参与一款足球游戏的开发,其中涉及到足球的各种运动轨迹和路径,比如射门的轨迹,高吊球,香蕉球的轨迹.最早的版本中马三是使用物理引擎加力的方式实现的足球各种运动,后来的版本中使用了根据物理 ...

  4. 【游戏开发进阶】教你Unity通过Jenkins实现自动化打包,打包这种事情就交给策划了(保姆级教程 | 命令行打包 | 自动构建)

    文章目录 一.前言 二.Jenkins简介 三.Jenkins的下载与安装 1.JDK下载与安装 2.Jenkins下载 3.Jenkins安装 4.Jenkins初始化 四.Jenkins的基本操作 ...

  5. HTML5游戏开发进阶指南

    <HTML5游戏开发进阶指南> 基本信息 作者: (印)香卡(Shankar,A.R.) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121212260 上架时间:2013- ...

  6. 世嘉MD游戏开发进阶篇【三】:向量归一化的实现及应用

    向量归一化是非常有用的,游戏中经常能用到,就说大家都见过的,FC魂斗罗的敌人发射子弹就能用到了,敌人向玩家发射子弹首先要获取到向量,这个向量不能直接作为方向去用,必须要经过归一化处理才行,经过归一化处 ...

  7. 学习3D游戏开发进阶之路

    笔者从事IT行业15年了,一直奋斗在一线编程,从普通程序员逐步成长到上市公司技术总监,目前在创业公司担任技术合伙人,主要负责公司整个项目团队的技术管理.在网上或者论坛上很多同学请教过我关于如何学习3D ...

  8. 【游戏开发实战】教你在Unity中实现模型消融化为灰烬飘散的效果(ShaderGraph | 消融 | 粒子系统 | 特效)

    文章目录 一.前言 二.ShaderGraph环境准备 三.模型准备:原神角色模型 四.实现思路 1.效果一的实现思路 2.效果二的实现思路 五.ShaderGraph具体实现 1.效果一 1.1.创 ...

  9. cocos2dx掼蛋_精通Cocos2d-x游戏开发(进阶卷) (王永宝著) 完整pdf扫描版[98MB]

    <精通Cocos2d-x游戏开发>分为<基础卷>和<进阶卷>两册.这两册都有明确的写作目的.<基础卷>专注于Cocos2d-x引擎基础,致力于让Coco ...

  10. 《Unity 3.x游戏开发实例》一1.5 欢迎来到Unity 3D

    本节书摘来异步社区<Unity 3.x游戏开发实例>一书中的第1章,第1.5节,作者: [加]Ryan Henson Creighton 译者: 师蓉 责编: 陈冀康,更多章节内容可以访问 ...

最新文章

  1. 单目深度估计方法综述
  2. ModuleNotFoundError: No module named ‘pandas.rpy‘
  3. 理解 IEnumerable 与 IEnumerator
  4. 信道仿真java_Matlab 瑞利信道仿真
  5. python polar contour_Python将2darray的最后一行和第二列添加到最后位置 - Polar Contour图...
  6. Python的序列切片
  7. 也谈ORACLE备份与恢复
  8. java 对象的定义是_浅析Java编程中类和对象的定义
  9. RxJava中BehaviorSubject适合的使用场景
  10. re模块(* * * * *)正则表达式
  11. 做折线图坐标轴数字_Excel折线图表的另类表达制作?牛闪君使用了双线表达法来完成...
  12. 160 - 51 DueList.6
  13. Coursera自动驾驶课程第14讲:Linear and Nonlinear Kalman Filters
  14. 如何给U盘设置一张妖娆又骚气的图标
  15. Dogeswap上线18小时TVL突破300万美金
  16. 关于问题PageNotFound.noHandlerFound No mapping found for HTTP的解决
  17. python2和3切换_python2和python3切换
  18. unity linux桌面环境,现在仍然可以在Ubuntu 20.04上安装Unity桌面环境
  19. 海康、大华设备搜索大概实现原理
  20. leetcode 1336解题思路

热门文章

  1. html如何修改title前的小图标
  2. 金山的 wifi共享android手机怎莫共享台式机3g无线网络,巧妙开启笔记本WiFi共享 手机上网有神助...
  3. 【windows10】宽带拨号连接报错720的解决办法
  4. 虚拟示波器---匿名上位机
  5. html加载fbx模型,[Unity菜鸟] FBX模型动画提取
  6. 城市地铁类毕业论文文献包含哪些?
  7. python编程a的x次方_python平方-Python,平方
  8. 刘宇凡:我是一只特立独行的猪
  9. 超级账本HyperLedger的Fabric-CA的使用(两个组织一个Orderer三个Peer),带视频演示
  10. 7个引人注目的创新物联网应用