B ( t ) = ∑ i = 0 n C n i ∗ P i ∗ ( 1 − t ) ( n − i ) ∗ t i ( t ∈ [ 0 , 1 ] ) B(t) = \sum_{i=0}^n C_n^i * P_i * (1-t)^{(n-i)} * t^i (t\in[0,1]) B(t)=i=0∑nCni∗Pi∗(1−t)(n−i)∗ti(t∈[0,1])
///
/// 求阶乘
///
///
///
public static int Factorial(this int n)
{
if (n == 0)
{
return 1;
}
return n * (n - 1).Factorial();
}
///
/// 求组合
///
///
///
///
public static int Combination(this int n, int m)
{
return n.Factorial() / (m.Factorial() * (n - m).Factorial());
}
///
///
///
///
///
///
private static Vector3 GetBezierPoint(float t, params Vector3[] p)
{
int n = p.Length - 1;
float u = 1 - t;
Vector3 result = Vector3.zero;
for (int i = 0; i < p.Length; i++)
{
result += p[i] * Mathf.Pow(u, n - i) * Mathf.Pow(t, i) * Combination(n, i);
}
return result;
}
///
///
///
///
///
///
public static void DrawBezier(this LineRenderer line, List<Vector3> pos, params Vector3[] target)
{
if (target == null || target.Length <= 0) return;
if (pos == null)
{
pos = new List<Vector3>();
}
pos.Clear();
float resolution = 0.01f;
int loops = Mathf.FloorToInt(1f / resolution);
for (int i = 0; i <= loops; i++)
{
pos.Add(GetBezierPoint(i * resolution, target));
}
line.positionCount = pos.Count;
line.SetPositions(pos.ToArray());
}
公式推导过程
由上述公式可推出经过四个点( P 0 P_0 P0, P 1 P_1 P1, P 2 P_2 P2, P 3 P_3 P3)的曲线方程为
B ( t ) = a + b ∗ t + c ∗ t 2 + d ∗ t 3 B(t) = a + b*t + c*t^2 + d*t^3 B(t)=a+b∗t+c∗t2+d∗t3
其切线方程为
C ( t ) = b + 2 ∗ c ∗ t + 3 ∗ d ∗ t 2 C(t) = b + 2*c* t + 3 * d * t^2 C(t)=b+2∗c∗t+3∗d∗t2
满足如下几个条件
(1) B ( 0 ) = P 1 B(0) = P_1 B(0)=P1,故a = P 1 P_1 P1
(2) C ( 0 ) = P 2 − P 1 2 , 故 b = P 2 − P 1 2 C(0) = \frac { P_2-P_1} {2},故 b = \frac{P_2-P_1}{2} C(0)=2P2−P1,故b=2P2−P1
(3) B ( 1 ) = P 2 , 故 a + b + c + d = P 2 B(1) = P_2,故 a+b+c+d = P_2 B(1)=P2,故a+b+c+d=P2
(4) C ( 1 ) = P 3 − P 1 2 , 故 b + 2 c + 3 d = P 3 − P 1 2 C(1) = \frac{ P_3 - P_1}{2},故b+2c+3d = \frac{P_3-P_1}{2} C(1)=2P3−P1,故b+2c+3d=2P3−P1
由上诉(1)(2)(3)(4)可推出
a = P 1 a= P_1 a=P1
b = P 2 − P 1 2 b =\frac{P_2-P_1}{2} b=2P2−P1
c = 2 P 0 − 5 P 1 + 4 P 2 − P 3 2 c=\frac{2P_0-5P_1+4P_2-P3}{2} c=22P0−5P1+4P2−P3
d = − P 0 + 3 P 1 − 3 P 2 + P 3 2 d=\frac{-P_0+3P_1-3P_2+P_3}{2} d=2−P0+3P1−3P2+P3
///
/// 绘制曲线
/// 每次取四个点:当前点的前一个点,当前点,当前点之后的第一个点,当前点之后的第二个点
///
///
/// 是否循环
/// 经过的点
public static void DrawCatmullRomSpline(this LineRenderer line, bool loop, params Vector3[] target)
{
if (target == null || target.Length <= 0) return;
List<Vector3> pos = new List<Vector3>();
int current = 0;
//绘制0~n-1
for (; current < target.Length - 1; current++)
{
if (current == 0) // 第一个点时,其相关的四个点为 0,0,1,2
{
GetCatmullRomSplinePos(pos, target[0], target[0], target[1], target[2]);
}
else if (current == target.Length - 2) // 为倒数第二个点: n-3,n-2,n-1,n-1
{
GetCatmullRomSplinePos(pos, target[current - 1], target[current], target[current + 1], target[current + 1]);
}
else
{
GetCatmullRomSplinePos(pos, target[current - 1], target[current], target[current + 1], target[current + 2]);
}
}
if (loop) // 循环就是绘制最后一个点与第一个点之间的线段 : n - 2,n-1,0,1
{
current = target.Length - 1;
GetCatmullRomSplinePos(pos, target[current - 1], target[current], target[0], target[1]);
}
line.positionCount = pos.Count;
line.SetPositions(pos.ToArray());
}
///
/// 绘制两点之间的曲线
///
///
///
///
///
///
private static void GetCatmullRomSplinePos(List<Vector3> pos, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float resolution = 0.01f;
int loops = Mathf.FloorToInt(1f / resolution);
for (int i = 0; i <= loops; i++)
{
pos.Add(GetCatmullRomPosition(i * resolution, p0, p1, p2, p3));
}
}
///
/// 会的对应t值的点
///
///
///
///
///
///
///
private static Vector3 GetCatmullRomPosition(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
Vector3 a = 2 * p1;
Vector3 b = p2 - p0;
Vector3 c = 2 * p0 - 5 * p1 + 4 * p2 - p3;
Vector3 d = -p0 + 3 * p1 - 3 * p2 + p3;
Vector3 pos = 0.5f * (a + (b * t) + (c * t * t) + (d * t * t * t));
return pos;
}