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

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

贝塞尔曲线原理

1、一阶贝塞尔曲线:

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

先来个标准公式 :

在这里插入图片描述

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

然后随便画个推导:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第1张图片

网上找到的示例图GIF:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第2张图片
上图中,P0为起点,Pn为终点,Pi为控制点

2、二阶贝塞尔曲线:

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

先来个标准公式:

在这里插入图片描述

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

然后随便画个推导:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第3张图片

网上找到的示例图GIF:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第4张图片
上图中,P0为起点,Pn为终点,Pi为控制点

3、三阶贝塞尔曲线:

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

先来个标准公式:

在这里插入图片描述

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

然后随便画个推导:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第5张图片

网上找到的示例图GIF:

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第6张图片

4、多阶贝塞尔曲线:

最终公式:

在这里插入图片描述

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

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

Unity 贝塞尔曲线(Beizer curve)的原理与运用_第7张图片

五阶贝塞尔曲线示例图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;

/// 
/// 贝塞尔曲线.
/// 
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);
        //}
	}

    /// 
    /// 贝塞尔曲线递归方法.
    /// 
    /// 插值比例 0.00-1.00
    /// 所有位置点
    /// 根据插值,得到的曲线最终点
    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);
    }
}

运行截图:
Unity 贝塞尔曲线(Beizer curve)的原理与运用_第8张图片
上方的代码是固定不动的贝塞尔曲线,要想动态,只需要稍作改动,Emmm…直接看下面代码吧.

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

/// 
/// 贝塞尔曲线 动态.
/// 
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());
	}

    /// 
    /// 贝塞尔曲线递归方法.
    /// 
    /// 插值比例 0.00-1.00
    /// 所有位置点
    /// 根据插值,得到的曲线最终点
    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物体,可以动态改变贝塞尔曲线.
Unity 贝塞尔曲线(Beizer curve)的原理与运用_第9张图片
完毕.

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

你可能感兴趣的:(Unity,3D数学,unity,游戏引擎)