导语:求曲线就是求曲线上的点
一、简单了解一下Bezier曲线的概念(个人理解)
给定空间中n+1个点坐标(向量)Pi (i∈N);并依次连接成一个多边形,称为控制多边形或特征方程。从该多边形的起点(P0)用一条线逼近每一条线段直到多边形的终点(P4)所形成的一条曲线,称该曲线为Bezier曲线。如1-1图所示:
当n=1时,为一阶Bezier曲线,有2个控制点
当n=2时,为二阶Bezier曲线,有3个控制
当n=3时,为三阶Bezier曲线,有4个控制点(以此类推)
二、用De CastelJau方法做几何图形(Bezier曲线的递推算法)
简述:假设控制多边形上的每一条线段上都有一个点 t ,(这个点可以从线段的起点移动到终点即 t 的值从0递增到1) 再将每条线段上的点 t 依次连接,形成新的控制多边形,一直循环,直到控制多边形为一条直线,则该直线上的 点 t 为Bezier曲线上的其中一个点。
用图形解释:
假设 t = 1/4 t∈[0,1] 即点 t 再每一条线段上的 1/4 处
分析与结论:
由此可知,当 t 值为固定值时,可以求出Bezier曲线上的一个点,则曲线可以用P(t)表示,此时 t 为变量,t∈[0,1] 。 P(t) 随着 t 的值变化而变化,从而得出很多的点,将这些点连接后就成为曲线。
由于用公式推导求出P(t)过于复杂,本人就简单分析如何求出 t 点坐标 的通项公式,因为在Unity中知道该通项公式就足够了。
已知一条线段的起点,终点坐标,然后用向量的减法即可轻易求出,如图所示
故,求t 点坐标的通项公式为: (1 - t )* P0 + t *P1 ,
其中P0为线段起点坐标,P1为线段终点坐标。
求其他线段点 t 坐标 也是用这个公式,这里就不证明了,通过这个公式,用代码中的循环语句求出每条线段上的 t 点 ,都放入一个集合中,通过这些点又形成了一个新的控制多边形,反复的求(递归),直到求出Bezier曲线上的点坐标。之后用循环再控制 t 的增量,求出P(t)。
注意:Bezier曲线的形状仅与控制多边形个顶点的相对位置有关,而与坐标系的选择无关。
三、用Unity实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Bezier曲线
/// </summary>
public class BezierDeCastelJau : MonoBehaviour
{
private LineRenderer lineRenderer; //线渲染器
public Transform fxiedPointParent; //定点所在的父物体
public Transform[] fixedPoint; //定点坐标集合
private List<Vector3> bezierPoints; //贝塞尔曲线上所有点的集合
void Start()
{
fxiedPointParent = this.transform;
lineRenderer = this.GetComponent<LineRenderer>();
fixedPoint = fxiedPointParent.GetComponentsInChildren<Transform>();
bezierPoints = new List<Vector3>();
}
void Update()
{
bezierPoints = BezierPosAll(fixedPoint);
lineRenderer.positionCount = bezierPoints.Count;
lineRenderer.SetPositions(bezierPoints.ToArray());
}
/// <summary>
/// 获取Bezier曲线上的一个点的坐标
/// </summary>
/// <param name="fixedPoint">定点集合</param>
/// <param name="t">定值</param>
/// <returns></returns>
private Vector3 BezierPos(List<Vector3> fixedPoint, float t)
{
if (fixedPoint.Count < 2)
return fixedPoint[0];
List<Vector3> temp = new List<Vector3>();
for (int i = 0; i < fixedPoint.Count - 1; i++)
{
Debug.DrawLine(fixedPoint[i], fixedPoint[i + 1], Color.yellow);
Vector3 tp = (1 - t) * fixedPoint[i] + t * fixedPoint[i + 1];
temp.Add(tp);
}
return BezierPos(temp, t);
}
/// <summary>
/// 获取Bezier曲线上所有点的坐标
/// </summary>
/// <param name="fixedPoint">定点坐标集合</param>
/// <returns></returns>
private List<Vector3> BezierPosAll(Transform[] fixedPoint)
{
List<Vector3> temp = new List<Vector3>();
List<Vector3> temp2 = new List<Vector3>();
for (int i = 1; i < fixedPoint.Length; i++)
{
temp2.Add(fixedPoint[i].position);
}
for (float i = 0; i <= 1; i += 0.01f)//假设 t 的增量为0.01
{
temp.Add(BezierPos(temp2, i));
}
return temp;
}
效果图为:
结语:
整篇文章内容仅为个人理解
Bezier曲线有两点不足:
1.不能局部修改,曲线上一个定点改变,整条曲线也会改变
2.多条Bezier曲线的拼接比较复杂
解决方法:使用B样条线
参考质料:
https://www.bilibili.com/video/BV1hs4115713?p=7&vd_source=2c6cb0727067c805957660ece8835dcb