前言
在unity中我们经常会用到一些曲线在解决自己的需求,比如说高空抛物显示物体运动轨迹,编辑运动轨迹,导弹发射路径等等。本小白作为unity初学者,接下来与大家一起分享下贝塞尔曲线的知识
一、贝塞尔曲线是什么?
Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线。 曲线定义:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。 1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名,称为贝塞尔曲线。
转自:http://blog.csdn.net/tianhai110/article/details/2203572
二、推导过程
1.一阶曲线
演示图:
公式如下:
B(t) = P0 + (P1-P0)t 整理如下:
解释如下:
如图,图中运动的黑点点就是我们要求的点,这个点一定是在曲线上(因为两个点只能形成一条直线,我们在这暂且把这条直线认为是一条特殊的“曲线”)的。公式中B(t)即为黑点的坐标,为了方便起见就叫Q,P0为起点,P1为终点,t为向量P0Q在向量P0P1中所占的比例,t∈[0,1],当t=1时,P0Q = P0P1。
2. 二阶曲线
有了一阶曲线的基础,二阶曲线就容易多了。
演示图:
公式如下:
如图所示,我们所求的黑点Q并不在两条向量上,而在这条绿色向量Q1Q2上,Q1为P0P1上的点,Q2为P1P2上的点,如何如何求点Q1和Q2呢?这就用到上面的公式了:
Q1 = (1-t)P0 + t(P1)
Q2 = (1-t)P1 + t(P2)
Q = (1-t)Q1 + tQ2
t∈[0,1]
最终整合公式,并将Q用B(t)代替,得到下面公式:
tips:
细心的老爷们发现没有,P0P1为曲线在P0处的切线,P1P2P为曲线在P2处的切线,绿色线段为曲线在Q点的切线。
3.三阶曲线
三阶曲线和二阶曲线有异曲同工之处
演示图:
公式:
用计算二阶的方法得出下面公式
4.多阶曲线
四阶演示图:
五阶演示图:
通用公式:
用二项式表达为
三、案例展示(三阶曲线)
代码:
using System.Collections.Generic;
using UnityEngine;
public class DrawBezierLine : MonoBehaviour
{
public Transform[] fourPoints = new Transform[4];//四个拖拽点的物体transform
int bezierPointCounts = 10;//两个拖拽点之间贝塞尔点的个数
//计算并获得贝塞尔点的集合
List<Vector3> GetBezierPoints()
{
List<Vector3> pointList = new List<Vector3>();
for (int i = 0; i <= bezierPointCounts; i++)
{
if (fourPoints.Length >= 4)
{
Vector3 p0 = fourPoints[0].position;//第一个点
Vector3 p1 = fourPoints[1].position;//第二个点
Vector3 p2 = fourPoints[2].position;//第三个点
Vector3 p3 = fourPoints[3].position;//第四个点
float t = i / (bezierPointCounts * 1.0f);//所占比例
//1.利用一阶公式递推
Vector3 p0p1 = (1 - t) * p0 + t * p1;
Vector3 p1p2 = (1 - t) * p1 + t * p2;
Vector3 p2p3 = (1 - t) * p2 + t * p3;
Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
Vector3 bezierPoint = (1 - t) * p0p1p2 + t * p1p2p3;
2.利用通用公式二次项展开
//Vector3 bezierPoint = p0 * Mathf.Pow((1 - t), 3) + 3 * p1 * t * Mathf.Pow((1 - t), 2) + 3 * p2 * Mathf.Pow(t, 2) * (1 - t) + p3 * Mathf.Pow(t, 3);
pointList.Add(bezierPoint);
}
}
return pointList;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
//将四个拖拽点用蓝色绘制
if (fourPoints.Length >= 4)
{
for (int i = 0; i < fourPoints.Length - 1; i++)
{
Gizmos.DrawLine(fourPoints[i].position, fourPoints[i + 1].position);
}
}
//计算得到曲线上点的集合
List<Vector3> bezierPoints = GetBezierPoints();
//将计算出来的贝塞尔曲线上的点用红色绘制出来
Gizmos.color = Color.red;
for (int j = 0; j < bezierPoints.Count - 1; j++)
{
Gizmos.DrawLine(bezierPoints[j], bezierPoints[j + 1]);
}
}
}
附上成果: