贝塞尔曲线开发相关总结
提示:个人学习总结,如有错误,敬请指正。
文章目录
- 贝塞尔曲线开发相关总结
- 一、1-3阶贝塞尔曲线
- 1.一阶贝塞尔曲线
- 2.二阶贝塞尔曲线
- 3.三阶贝塞尔曲线
- 二、过定点的二阶贝塞尔曲线以及其升阶
- 1.过定点的二阶贝塞尔曲线
- 2.二阶贝塞尔曲线的升阶
- 三、三阶贝塞尔的分段
- 1.迭代求t
- 2.解方程求t
- 四、贝塞尔曲线的平行线
- 附:参考链接
一、1-3阶贝塞尔曲线
1.一阶贝塞尔曲线
一阶贝塞尔曲线公式:B(t) = P1 + (P2 − P1)t = P1(1−t)+ P2t, t∈[0,1]
一阶贝塞尔曲线是一条直线,在三阶贝塞尔中表现为调整点为起终止点
public Vector3 lineBezier(float t,Vector3 p0, Vector3 p1)
{
Vector3 a = p0;
Vector3 b = p1;
return a + (b - a) * t;
}
2.二阶贝塞尔曲线
二阶贝塞尔曲线公式:B(t) = P1(1 - t)2 + 2P2t(1 - t) + P3t2,t∈[0,1]
二阶贝塞尔曲线有一个调整点,但画出的贝塞尔曲线没有经过该调整点
/// <summary>
/// 获得二阶
/// </summary>
/// <param name="p0">起点</param>
/// <param name="p1">调整点</param>
/// <param name="p2">终点</param>
/// <param name="t"></param>
/// <returns></returns>
public Vector3 quardaticBezier(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
Vector3 a = p0;
Vector3 b = p1;
Vector3 c = p2;
Vector3 aa = a + (b - a) * t;
Vector3 bb = b + (c - b) * t;
return aa + (bb - aa) * t;
}
3.三阶贝塞尔曲线
三阶贝塞尔曲线公式:B(t) = P1(1 - t)3 + 3P2t(1 - t)2 + 3P3t2(1 - t) + P4t3,t∈[0,1]
/// <summary>
/// 获得三阶
/// </summary>
/// <param name="p0">起点</param>
/// <param name="p1">调整点</param>
/// <param name="p2">调整点</param>
/// <param name="p3">终点</param>
/// <param name="t"></param>
/// <returns></returns>
public Vector3 cubicBezier(float t,Vector3 p0, Vector3 p1, Vector3 p2,Vector3 p3)
{
Vector3 a = p0;
Vector3 b = p1;
Vector3 c = p2;
Vector3 d = p3;
Vector3 aa = a + (b - a) * t;
Vector3 bb = b + (c - b) * t;
Vector3 cc = c + (d - c) * t;
Vector3 aaa = aa + (bb - aa) * t;
Vector3 bbb = bb + (cc - bb) * t;
return aaa + (bbb - aaa) * t;
}
二、过定点的二阶贝塞尔曲线以及其升阶
1.过定点的二阶贝塞尔曲线
在二阶贝塞尔曲线中我们知道其有一个调整点但曲线本身不经过,但在开发中会要求使用一个经过曲线的点来控制曲线的变化。为了解决这个问题,我们可以设置一个新的调整点,使得产生的新曲线经过要求的点。
/// <summary>
/// 定点的二阶
/// </summary>
/// <param name="p0">起点</param>
/// <param name="p1">调整点</param>
/// <param name="p2">终点</param>
/// <param name="t"></param>
/// <returns></returns>
public Vector3 quardaticOverPointBezier(float t,Vector3 p0, Vector3 p1, Vector3 p2)
{
Vector3 a = p0;
Vector3 b = p1;
Vector3 c = p2;
//具体推导可以看后面的链接
b = p1 - Mathf.Sqrt((p0 - p1).magnitude * (p2 - p1).magnitude) * ((p0 - p1) / (p0 - p1).magnitude + (p2 - p1) / (p2 - p1).magnitude) / 2;
Vector3 aa = a + (b - a) * t;
Vector3 bb = b + (c - b) * t;
return aa + (bb - aa) * t;
}
2.二阶贝塞尔曲线的升阶
由于n次贝塞尔曲线可以转换为一个形状完全相同的n+1次贝塞尔曲线
因此可以根据过定点的二阶贝塞尔转化成过顶点三阶贝塞尔
//二阶转三阶:
A= p0;
B= p0/3+2*p1/3;
C= 2*p1/3+p2/3;
D= p2;
//后续照搬上面的三阶贝塞尔
三、三阶贝塞尔的分段
如果在三阶贝塞尔曲线上添加一个点,那么由此生成的两段曲线依然是贝塞尔曲线吗?
答案是是的,链接中已给出了求添加点调整点的公式,但新的问题是如何求该点在贝塞尔曲线中的t值?
1.迭代求t
找到一段范围然后再新的分成新的范围去找满足条件的t值
迭代能保证一定的精度,但是耗时,在频繁求t值可能会出现崩溃
/// <summary>
///根据贝塞尔曲线上的点求t
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <param name="p">该点</param>
/// <param name="startT"></param>
/// <param name="endT"></param>
/// <returns></returns>
private float GetTFormCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3,Vector3 p,float startT = 0,float endT = 1)
{
var t = startT;
var minDistance = Mathf.Infinity;
var minDistanceT = 0f;
var step = (endT - startT) / 100;
for (int i = 0; i < 100; i++)
{
var point = cubicBezier(t,p0, p1, p2,p3);
var dst = Vector3.Distance(point, p);
if (dst < minDistance)
{
minDistance = dst;
minDistanceT = t;
}
if (dst < 0.0001)
{
return t;
}
t += step;
}
return GetTFormCurve(p0, p1, p2,p3, p, minDistanceT - step, minDistanceT + step);
}
2.解方程求t
我尝试找过三阶贝塞尔的解方程,但是难以实现。之前讲过可以用二阶升三阶,二阶贝塞尔解方程是一件容易的事情,那么可以从二阶贝塞尔曲线出发
二阶贝塞尔解方程是一元二次方程,得出在[0,1]之间的解,若两个则求坐标与点最近的那个。
//z0,z1,z2为p1,p2,p3的x值,tp为目标x
//curvedata中存储一元二次方程解的情况
//tt单解,tt1,tt2为解
private curvedata Solution(float z0, float z1, float z2, float tp)
{
var a = z0 - z1 * 2 + z2;
var b = 2 * (z1 - z0);
var c = z0 - tp;
var tt = 0f;
curvedata result = new curvedata();
if (a == 0 && b != 0)
{
tt = -c / b;
}
else
{
var sq = Math.Sqrt(b * b - 4 * a * c);
var tt1 = (sq - b) / (2 * a);
var tt2 = (-sq - b) / (2 * a);
if ((tt1 <= 1 && tt1 >= 0) && (tt2 <= 1 && tt2 >= 0))
{
result.tt1 = (float)tt1;
result.tt1 = (float)tt2;
return result;
}
else if (tt1 <= 1 && tt1 >= 0)
{
tt = (float)tt1;
result.t = tt;
}
else
{
tt = (float)tt2;
result.t = tt;
}
}
return result;
}
经过上面进行判断即可求出t值
//A、B、C、D为上文的二阶转三阶
//I,J 即为所求
var F = A * (1 - t) + B * t;
var G = B * (1 - t) + C * t;
var H = C * (1 - t) + D * t;
var I = F * (1 - t) + G * t;
var J = G * (1 - t) + H * t;
亲测得出的结果和迭代所求有可忽略的误差。
至此已经求出点的两个调整点,可实现在一段由二阶升三阶的贝塞尔曲线添加一个点,形成两段可调节的三阶贝塞尔曲线。
四、贝塞尔曲线的平行线
TODO