Bezier曲线与Catmull-Rom曲线

一、N次贝塞尔

1.一般参数公式

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=0nCniPi(1t)(ni)ti(t[0,1])

2.代码

(1)求   n ! \ n!  n! 方法

/// <summary>
/// 求阶乘
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static int Factorial(this int n)
{
    if (n == 0)
    {
        return 1;
    }
    return n * (n - 1).Factorial();
}

(2)求组合   C n m \ C_n^m  Cnm 方法

/// <summary>
/// 求组合
/// </summary>
/// <param name="n"></param>
/// <param name="m"></param>
/// <returns></returns>
public static int Combination(this int n, int m)
{
	return n.Factorial() / (m.Factorial() * (n - m).Factorial());
}

(3)获取Bezier曲线的点

/// <summary>
/// 
/// </summary>
/// <param name="t"></param>
/// <param name="p"></param>
/// <returns></returns>
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;
}

(4) 创建曲线

/// <summary>
/// 
 /// </summary>
 /// <param name="line"></param>
 /// <param name="pos"></param>
 /// <param name="target"></param>
 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());
 }

Catmull-Rom曲线

1、推导公式

公式推导过程
由上述公式可推出经过四个点( 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+bt+ct2+dt3
其切线方程为
C ( t ) = b + 2 ∗ c ∗ t + 3 ∗ d ∗ t 2 C(t) = b + 2*c* t + 3 * d * t^2 C(t)=b+2ct+3dt2
满足如下几个条件

(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)=2P2P1,b=2P2P1
(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)=2P3P1,b+2c+3d=2P3P1
由上诉(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=2P2P1
c = 2 P 0 − 5 P 1 + 4 P 2 − P 3 2 c=\frac{2P_0-5P_1+4P_2-P3}{2} c=22P05P1+4P2P3
d = − P 0 + 3 P 1 − 3 P 2 + P 3 2 d=\frac{-P_0+3P_1-3P_2+P_3}{2} d=2P0+3P13P2+P3

2、代码

/// <summary>
/// 绘制曲线
/// 每次取四个点:当前点的前一个点,当前点,当前点之后的第一个点,当前点之后的第二个点
/// </summary>
/// <param name="line"></param>
/// <param name="loop">是否循环</param>
/// <param name="target">经过的点</param>
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());
}

/// <summary>
/// 绘制两点之间的曲线
/// </summary>
/// <param name="pos"></param>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
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));
    }
}

/// <summary>
/// 会的对应t值的点
/// </summary>
/// <param name="t"></param>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns></returns>
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;
}
  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值