【Unity】貝塞爾曲線關於點、長度、切線計算在 Unity中的C#實現

寫在前面

最近給項目做了個路徑編輯,基本思路是滿足幾個基本需求:
【額外說明】其實本篇和這個沒關係,可以跳過“寫在前面”這部分,跨到正文部分

編輯時:
① 隨意增減、插入、刪除路點,只要路點數量大於1,繪製曲線,曲線必定經過路點。
② 調整路點的Forward方向,控制曲線的入線切線方向、出線切線方向。這樣可以通過旋轉直接調整曲線形狀。
③ 控制Forward方向的基礎上,增添描述切線“強度”的變量,來進一步控制曲線的形狀。
④ 可以指定每段曲線的邏輯長度,程序提供一個曲線近似長度幫助確定邏輯長度。
⑤ 導出曲線的數據。

運行時,可以根絕數據:
⑥ 對路點進行從0開始的編號,使用0.01~0.99來描述在某段曲線上的位置(邏輯上的),然後轉化成爲實際的座標。
⑦ 可以獲得在曲線上任意一點的切線方向。


正文


從路點、路點forward到三階貝塞爾曲線的四個點

關於貝塞爾曲線,可見下文
貝塞爾曲線
貝塞爾曲線在線演示

每兩個路點作爲三階貝塞爾曲線的起點(第0個點P0)和終點(第3個點P3)。
這裏寫圖片描述

起點路點的Forward方向乘以“強度”的變量,再加上起點座標,作爲第1個點P1。
起點路點的Forward反方向乘以“強度”的變量,再加上起點座標,作爲第2個點P2。
這裏寫圖片描述
得到 p0~p4這四個點之後,即可使用三階貝塞爾曲線的相關公式了

繪製貝塞爾曲線,圖中紅色線條部分
這裏寫圖片描述


三階貝塞爾曲線線上某點座標(Unity & C#)

三階貝塞爾曲線公式(來自百度百科)
這裏寫圖片描述

形參中的 t, p0, p1, p2, p3 分別對應公式中的 t 以及 p0~p3

    public Vector3 BezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
        float uuu = uu * u;
        float ttt = tt * t;

        Vector3 p = uuu * p0;
        p += 3 * uu * t * p1;
        p += 3 * u * tt * p2;
        p += ttt * p3;

        return p;
    }

三階貝塞爾曲線的近似長度(Unity & C#)

計算長度的思路:
在貝塞爾曲線上取n個點,計算點之間的直線長度,進行加和,從而取得一個曲線的近似長度。取點越多這個長度越趨向於精確。

形參中的p0, p1, p2, p3 分別對應公式中的 p0~p3。pointCount代表取點個數,默認30。

    public float BezierLength(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, int pointCount = 30)
    {
        if (pointCount < 2)
        {
            return 0;
        }

        //取點 默認 30個
        float length = 0.0f;
        Vector3 lastPoint = BezierPoint(0.0f / (float)pointCount, p0, p1, p2, p3);
        for (int i = 1; i <= pointCount; i++)
        {
            Vector3 point = BezierPoint((float)i/(float)pointCount, p0, p1, p2, p3);
            length += Vector3.Distance(point, lastPoint);
            lastPoint = point;
        }
        return length;
    }

三階貝塞爾曲線線上某點的切線(Unity & C#)

在已知貝塞爾曲線表達式的情況下,想要知道某點的切線,對曲線求導。
可得:
這裏寫圖片描述

整理後可得
這裏寫圖片描述

整體公式構成只有p0~p3 以及 t 和 (1-t),爲了表達式更直接,不進行進一步的整理。
所以得到下面的代碼

形參中的 t, p0, p1, p2, p3 分別對應公式中的 t 以及 p0~p3

    public Vector3 BezierTangent(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        float u = 1 - t;
        float uu = u * u;
        float tu = t * u;
        float tt = t * t;

        Vector3 P = p0 * 3 * uu * (-1.0f);
        P += p1 * 3 * (uu - 2 * tu);
        P += p2 * 3 * (2 * tu - tt);
        P += p3 * 3 * tt;        

        //返回單位向量
        return P.normalized;
    }

寫在後面

主要參照:
Unity遊戲中使用貝塞爾曲線
求二次、三次貝塞爾曲線的某個時間的位置及切線方向

轉載請註明,出自喵喵丸的博客http://blog.csdn.net/u011643833/article/details/78540554

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章