Unity 贝塞尔曲线(Beizer curve)的原理与运用

前言:现在使用各种搜索引擎 搜索贝塞尔曲线,都会有很多介绍。这里自己写一篇博客,只是记录一下自己的学习过程与运用方法,方便后续回忆。

贝塞尔曲线原理
1、一阶贝塞尔曲线:

一阶贝塞尔曲线,其实就是找一根线中的其中一点。通过把这些点集合连接,就是一阶贝塞尔曲线(也是当前的线段)。

先来个标准公式 😊:

在这里插入图片描述

上述公式中,B(t)为t时间下点的坐标 求和

然后随便画个推导😏:

网上找到的示例图GIF😜:

上图中,P0为起点,Pn为终点,Pi为控制点

2、二阶贝塞尔曲线:

二阶贝塞尔曲线,就是先在所知的相连的两个点之间找出对应点,然后再把这两个点连线,找出最终的点。
通过把找出来的点集合,连线,就是二阶贝塞尔曲线。(点集合越多,曲线越精细,后面C#代码有体现)

先来个标准公式😊:

在这里插入图片描述

上述公式中,B(t)为t时间下点的坐标 求和

然后随便画个推导😏:

网上找到的示例图GIF😜:

上图中,P0为起点,Pn为终点,Pi为控制点

3、三阶贝塞尔曲线:

原理跟上面的二阶贝塞尔曲线类似,只是多了一重。(点集合越多,曲线越精细,后面C#代码有体现)

先来个标准公式😊:

在这里插入图片描述

上述公式中,B(t)为t时间下点的坐标 求和

然后随便画个推导😏:

网上找到的示例图GIF😜:


4、多阶贝塞尔曲线:

最终公式😎:

在这里插入图片描述

上述公式中,B(t)为t时间下点的坐标 求和

四阶贝塞尔曲线示例图GIF😜:

五阶贝塞尔曲线示例图GIF😜:


贝塞尔曲线运用
1、直接上核心代码.
    // 一阶贝塞尔曲线,参数P0、P1、t对应上方原理内的一阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, float t)
    {
        return (1 - t) * p0 + t * p1;
    }

    // 二阶贝塞尔曲线,参数对应上方原理内的二阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t)
    {
        Vector3 p0p1 = (1 - t) * p0 + t * p1;
        Vector3 p1p2 = (1 - t) * p1 + t * p2;
        Vector3 temp = (1 - t) * p0p1 + t * p1p2;
        return temp;
    }
    
    // 三阶贝塞尔曲线,参数对应上方原理内的三阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
    {
        Vector3 temp;
        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;
        temp = (1 - t) * p0p1p2 + t * p1p2p3;
        return temp;
    }

    // 多阶贝塞尔曲线,使用递归实现.
    public Vector3 Bezier(float t, List<Vector3> p)
    {
        if (p.Count < 2)
            return p[0];
        List<Vector3> newp = new List<Vector3>();
        for (int i = 0; i < p.Count - 1; i++)
        {
            Debug.DrawLine(p[i], p[i + 1]);
            
            Vector3 p0p1 = (1 - t) * p[i] + t * p[i + 1];
            newp.Add(p0p1);
        }
        return Bezier(t, newp);
    }

2、Unity案例.

使用LineRenderer来绘制线,Emmm…比较简单,还是直接上代码吧.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 贝塞尔曲线.
/// </summary>
public class BezierTest : MonoBehaviour 
{
    private LineRenderer m_LineRenderer;    //线渲染器.

    [SerializeField]
    [Header("位置点的父物体")]
    private Transform foceObjParent;        //位置点的父物体.

    private List<Vector3> foceObjList;      //所有的位置点集合.

	void Start () 
    {
        //初始化.
        m_LineRenderer = this.gameObject.GetComponent<LineRenderer>();
        foceObjList = new List<Vector3>();
        Transform[] child_Transform = foceObjParent.GetComponentsInChildren<Transform>();
        for (int i = 1; i < child_Transform.Length; i++)
        {
            foceObjList.Add(child_Transform[i].position);
        }

        //贝塞尔曲线.
        List<Vector3> tempList = new List<Vector3>();
        for (float i = 0; i <= 1; i += 0.01f)  
        {
            tempList.Add(BezierMethod(i, foceObjList)); //获得插值后的每个点.
        }
        //tempList.Add(BezierMethod(1, foceObjList));   //for循环中,由于是浮点数变量,有时候数值不准确.(i+=0.1f时,则会少执行最后一次)
        m_LineRenderer.positionCount = tempList.Count;
        m_LineRenderer.SetPositions(tempList.ToArray());
	}
	
	void Update () 
    {
        //插值10次,Scene场景查看曲线.
        for (float i = 0; i < 1; i += 0.1f)
        {
            Debug.DrawLine(BezierMethod(i, foceObjList), BezierMethod(i + 0.1f, foceObjList), Color.blue);
        }
        //for (float i = 0; i < 1; i += 0.01f)
        //{
        //    Debug.DrawLine(BezierMethod(i, foceObjList), BezierMethod(i + 0.01f, foceObjList), Color.blue);
        //}
	}

    /// <summary>
    /// 贝塞尔曲线递归方法.
    /// </summary>
    /// <param name="t">插值比例 0.00-1.00</param>
    /// <param name="foceList">所有位置点</param>
    /// <returns>根据插值,得到的曲线最终点</returns>
    private Vector3 BezierMethod(float t, List<Vector3> foceList)
    {
        if (foceList.Count < 2)
            return foceList[0];

        List<Vector3> temp = new List<Vector3>();
        for (int i = 0; i < foceList.Count - 1; i++)
        {
            Debug.DrawLine(foceList[i], foceList[i + 1], Color.yellow);
            Vector3 proportion = (1 - t) * foceList[i] + t * foceList[i + 1];
            temp.Add(proportion);
        }

        return BezierMethod(t, temp);
    }
}

运行截图:
在这里插入图片描述
上方的代码是固定不动的贝塞尔曲线,要想动态,只需要稍作改动,Emmm…直接看下面代码吧.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 贝塞尔曲线 动态.
/// </summary>
public class BezierDynamicTest : MonoBehaviour {

    private LineRenderer m_LineRenderer;    //线渲染器.

    [SerializeField]
    [Header("位置点的父物体")]
    private Transform foceObjParent;        //位置点的父物体.
    private Transform[] child_Transform;    //所有的位置点.

    private List<Vector3> foceObjList;      //当前的位置点集合.
    private List<Vector3> bezierList;       //贝塞尔曲线 点集合.

	void Start () 
    {
        m_LineRenderer = this.gameObject.GetComponent<LineRenderer>();

        child_Transform = foceObjParent.GetComponentsInChildren<Transform>();

        foceObjList = new List<Vector3>();
        bezierList = new List<Vector3>();
	}
	
	void Update () 
    {
        foceObjList.Clear();
        for (int i = 1; i < child_Transform.Length; i++)
        {
            foceObjList.Add(child_Transform[i].position);
        }

        bezierList.Clear();
        for (float i = 0; i <= 1; i += 0.01f)
        {
            bezierList.Add(BezierMethod(i, foceObjList));
        }
        //bezierList.Add(BezierMethod(1, foceObjList));   //for循环中,由于是浮点数变量,有时候数值不准确.(i+=0.1f时,则会少执行最后一次)

        m_LineRenderer.positionCount = bezierList.Count;
        m_LineRenderer.SetPositions(bezierList.ToArray());
	}

    /// <summary>
    /// 贝塞尔曲线递归方法.
    /// </summary>
    /// <param name="t">插值比例 0.00-1.00</param>
    /// <param name="foceList">所有位置点</param>
    /// <returns>根据插值,得到的曲线最终点</returns>
    private Vector3 BezierMethod(float t, List<Vector3> foceList)
    {
        if (foceList.Count < 2)
            return foceList[0];

        List<Vector3> temp = new List<Vector3>();
        for (int i = 0; i < foceList.Count - 1; i++)
        {
            Debug.DrawLine(foceList[i], foceList[i + 1], Color.yellow);
            Vector3 proportion = (1 - t) * foceList[i] + t * foceList[i + 1];
            temp.Add(proportion);
        }

        return BezierMethod(t, temp);
    }
}

再来张动态贝塞尔曲线的截图,拖动Cube物体,可以动态改变贝塞尔曲线.
在这里插入图片描述
完毕.

ref:
公式
GIF示例图
markdown文本显示emoji表情

  • 27
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值