Foreword
好記性不如爛筆頭。
BezierCurve簡介
在數(shù)學(xué)的數(shù)值分析領(lǐng)域中俩滥,貝塞爾曲線(英語:Bézier curve,亦作“貝塞爾”)是計(jì)算機(jī)圖形學(xué)中相當(dāng)重要的參數(shù)曲線错忱。更高維度的廣泛化貝塞爾曲線就稱作貝塞爾曲面航背,其中貝塞爾三角是一種特殊的實(shí)例棱貌。 —— 維基百科
婚脱。本篇只討論曲線。
構(gòu)建Bezier曲線
-
線性曲線 由兩個(gè)頂點(diǎn)構(gòu)成错森。線性貝塞爾曲線函數(shù)中的t會(huì)經(jīng)過由P0至P1的B(t)所描述的曲線涩维。例如當(dāng)t=0.25時(shí),B(t)即一條由點(diǎn)P0至P1路徑的四分之一處蜗侈。就像由0至1的連續(xù)t踏幻,B(t)描述一條由P0至P1的直線戳杀。高階的曲線全部基于這個(gè)線性插值信卡。(Unity中向量的插值計(jì)算方法Vector3.Lerp可以完美實(shí)現(xiàn))
線性貝塞爾曲線演示動(dòng)畫傍菇,t在[0,1]區(qū)間
-
二次曲線 由三個(gè)點(diǎn)構(gòu)成。建構(gòu)二次貝塞爾曲線须妻,可以使中介點(diǎn)Q0和Q1作為由0至1的t:
- 由P0至P1的連續(xù)點(diǎn)Q0荒吏,描述一條線性貝塞爾曲線绰更。
- 由P1至P2的連續(xù)點(diǎn)Q1锡宋,描述一條線性貝塞爾曲線。
- 由Q0至Q1的連續(xù)點(diǎn)B(t)徐钠,描述一條二次貝塞爾曲線尝丐。
二次貝塞爾曲線的結(jié)構(gòu)二次貝塞爾曲線演示動(dòng)畫爹袁,t在[0,1]區(qū)間 -
高階曲線 建構(gòu)高階曲線失息,便需要相應(yīng)更多的中介點(diǎn)盹兢。
- 三次貝塞爾曲線
三次貝塞爾曲線的結(jié)構(gòu)三次貝塞爾曲線演示動(dòng)畫蛤迎,t在[0,1]區(qū)間- 四次貝塞爾曲線
四次貝塞爾曲線的結(jié)構(gòu)四次貝塞爾曲線演示動(dòng)畫替裆,t在[0,1]區(qū)間- 更高階同理辆童,需要更多的中介點(diǎn)把鉴,詳細(xì)見代碼塊儿咱。
應(yīng)用與代碼
二次貝塞爾曲線
//pointList 為頂點(diǎn)集合。 point1怠缸,point2揭北,point3 為構(gòu)建曲線的三個(gè)頂點(diǎn)
//Vector3.Lerp 為 UnityEngine 中的API吏颖。通過傳入兩點(diǎn)和之間的插值(0~1)得到一個(gè)新的三維向量
//vertexCount 為構(gòu)建曲線的頂點(diǎn)數(shù),此數(shù)值越大曲線越平滑
public static Vector3[] GetBezierCurveWithThreePoints(Vector3 point_1, Vector3 point_2, Vector3 point_3, int vertexCount)
{
List<Vector3> pointList = new List<Vector3>();
for (float ratio = 0; ratio <= 1; ratio += 1.0f / vertexCount)
{
//首先取前兩個(gè)點(diǎn)和后兩個(gè)點(diǎn)的線性插值疚俱。
Vector3 tangentLineVertex1 = Vector3.Lerp(point_1, point_2, ratio);
Vector3 tangentLineVertex2 = Vector3.Lerp(point_2, point_3, ratio);
//通過計(jì)算兩個(gè)點(diǎn)的插值得到曲線的頂點(diǎn)
Vector3 bezierPoint = Vector3.Lerp(tangentLineVertex1, tangentLineVertex2, ratio);
pointList.Add(bezierPoint);
}
pointList.Add(point_3);
return pointList.ToArray();
}
二次貝塞爾曲線unity中演示效果
二次貝塞爾曲線演示動(dòng)畫
高階貝塞爾曲線
//傳入頂點(diǎn)集合,得到高階的貝塞爾曲線瞧壮,頂點(diǎn)數(shù)量不限
//vertexCount 為構(gòu)建曲線的頂點(diǎn)數(shù),此數(shù)值越大曲線越平滑
public static Vector3[] GetBezierCurveWithUnlimitPoints(Vector3[] vertex, int vertexCount)
{
List<Vector3> pointList = new List<Vector3>();
pointList.Clear();
for (float ratio = 0; ratio <= 1; ratio += 1.0f / vertexCount)
{
pointList.Add(UnlimitBezierCurve(vertex, ratio));
}
pointList.Add(vertex[vertex.Length - 1]);
return pointList.ToArray();
}
public static Vector3 UnlimitBezierCurve(Vector3[] vecs, float t)
{
Vector3[] temp = new Vector3[vecs.Length];
for (int i = 0; i < temp.Length; i++)
{
temp[i] = vecs[i];
}
//頂點(diǎn)集合有多長陈轿,曲線的每一個(gè)點(diǎn)就需要計(jì)算多少次麦射。
int n = temp.Length - 1;
for (int i = 0; i < n; i++)
{
//依次計(jì)算各兩個(gè)相鄰的頂點(diǎn)的插值,并保存蛔琅,每次計(jì)算都會(huì)進(jìn)行降階罗售。剩余多少階計(jì)算多少次钩述。直到得到最后一條線性曲線。
for (int j = 0; j < n - i; j++)
{
temp[j] = Vector3.Lerp(temp[j], temp[j + 1], t);
}
}
//返回當(dāng)前比例下曲線的點(diǎn)
return temp[0];
}
高階貝塞爾曲線unity中演示效果
高階貝塞爾曲線演示動(dòng)畫
整體腳本代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class BezierCurvePointRenderer : MonoBehaviour
{
public Transform point1;
public Transform point2;
public Transform point3;
public LineRenderer lineRenderer;
public int vertexCount;
public Transform[] positions;
private List<Vector3> pointList;
private void Start()
{
pointList = new List<Vector3>();
}
private void Update()
{
//BezierCurveWithThree();
BezierCurveWithUnlimitPoints();
lineRenderer.positionCount = pointList.Count;
lineRenderer.SetPositions(pointList.ToArray());
}
private void BezierCurveWithThree()
{
pointList.Clear();
for (float ratio = 0; ratio <= 1; ratio += 1.0f / vertexCount)
{
Vector3 tangentLineVertex1 = Vector3.Lerp(point1.position, point2.position, ratio);
Vector3 tangentLineVertex2 = Vector3.Lerp(point2.position, point3.position, ratio);
Vector3 bezierPoint = Vector3.Lerp(tangentLineVertex1, tangentLineVertex2, ratio);
pointList.Add(bezierPoint);
}
pointList.Add(point3.position);
}
public void BezierCurveWithUnlimitPoints()
{
pointList.Clear();
for (float ratio = 0; ratio <= 1; ratio += 1.0f / vertexCount)
{
pointList.Add(UnlimitBezierCurve(positions, ratio));
}
pointList.Add(positions[positions.Length - 1].position);
}
public Vector3 UnlimitBezierCurve(Transform[] trans, float t)
{
Vector3[] temp = new Vector3[trans.Length];
for (int i = 0; i < temp.Length; i++)
{
temp[i] = trans[i].position;
}
int n = temp.Length - 1;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i; j++)
{
temp[j] = Vector3.Lerp(temp[j], temp[j + 1], t);
}
}
return temp[0];
}
private void OnDrawGizmos()
{
#region 無限制頂點(diǎn)數(shù)
Gizmos.color = Color.green;
for (int i = 0; i < positions.Length - 1; i++)
{
Gizmos.DrawLine(positions[i].position, positions[i + 1].position);
}
Gizmos.color = Color.red;
Vector3[] temp = new Vector3[positions.Length];
for (int i = 0; i < temp.Length; i++)
{
temp[i] = positions[i].position;
}
int n = temp.Length - 1;
for (float ratio = 0.5f / vertexCount; ratio < 1; ratio += 1.0f / vertexCount)
{
for (int i = 0; i < n - 2; i++)
{
Gizmos.DrawLine(Vector3.Lerp(temp[i], temp[i + 1], ratio), Vector3.Lerp(temp[i + 2], temp[i + 3], ratio));
}
}
#endregion
//#region 頂點(diǎn)數(shù)為3
//Gizmos.color = Color.green;
//Gizmos.DrawLine(point1.position, point2.position);
//Gizmos.color = Color.green;
//Gizmos.DrawLine(point2.position, point3.position);
//Gizmos.color = Color.red;
//for (float ratio = 0.5f / vertexCount; ratio < 1; ratio += 1.0f / vertexCount)
//{
// Gizmos.DrawLine(Vector3.Lerp(point1.position, point2.position, ratio), Vector3.Lerp(point2.position, point3.position, ratio));
//}
//#endregion
}
}