环境:Unity2021.1.14 语言:C#
本文源代码可以在https://github.com/anguangzhihen/TestOdinInspector中的TestCurve场景中找到。
Bezier曲线和Catmull-Rom曲线是工程中常见的曲线实现方式,他们本身原理十分简单,只是个多项式方程组,拿到公式带入就能实现。
Bezier曲线和Catmull-Rom曲线之间可以相互转换,所以本篇内容只针对Bezier曲线进行说明,Catmull-Rom本质上是一样的。
最近在工作中需要使用Bezier曲线来控制镜头运动,实现后在跟美术反复沟通后发现,他们想要时间曲线直接控制镜头的运动速度,但是现在Bezier的实现中传入参数t在曲线上运动是有快有慢的,这会造成美术在控制时间曲线时非常不直观。
因此保证传入参数使得能在曲线上匀速运动是非常重要的。
后面查了很多资料后发现了一种工业界常用的方案能解决该问题——参数曲线。
一般开发中最常用的Bezier曲线为三次方公式:
具体实现也非常简单:
public static Vector3 GetBezierPoint(Vector3[] points, float t)
{
Vector3 p0 = points[0];
Vector3 p1 = points[1];
Vector3 p2 = points[2];
Vector3 p3 = points[3];
float rest = (1f - t);
Vector3 newPos = Vector3.zero;
newPos += p0 * rest * rest * rest;
newPos += p1 * t * 3f * rest * rest;
newPos += p2 * 3f * t * t * rest;
newPos += p3 * t * t * t;
return newPos;
}
实现效果:
可以看到采样小球并不是均匀分布的。
而我们的目标就是获得一个函数t=f(s),使得传入的s保证是匀速运动的,s具体来说应该是曲线长度的百分比,最终目标就变成了获取弧长。
包括Bezier曲线的很多曲线都能表示为以下形式:
使用Bezier曲线公式进行推导就能获得具体的A、B、C、D:
public static ParametricCurve CreateByBezier(Vector3[] points)
{
ParametricCurve curve = new ParametricCurve();
curve.points = points;
curve.A = -1 * points[0] + 3 * points[1] - 3 * points[2] + points[3];
curve.B = 3 * points[0] - 6 * points[1] + 3 * points[2];
curve.C = -3 * points[0] + 3 * points[1];
curve.D = points[0];
return curve;
}
然后对该公式求导获取绝对值后,求0到t的积分就能获得从0到t处的弧长:
积分的求解非常复杂,《数值分析》中对这块内容有详细介绍,我只是简单的学习了一下就不班门弄斧了。不过具体应用起来还是很方便的,我们这边采用Gauss-Legendre求积公式:
public float GetArcLength(float t)
{
var halfT = t / 2f;
float sum = 0f;
foreach (var wx in gaussWX)
{
var w = wx[0];
var x = wx[1];
// 权值w乘以公式带入特定值x
sum += w * GetPointDer(halfT * x + halfT).magnitude;
}
sum *= halfT;
return sum;
}
获得了该公式后,我们实际已知的是s,而需要求t,这个时候使用牛顿迭代法迭代3、4次就可以快速的获得精确的t:
其中t0是初始迭代值,t1是下一个迭代值,这边我们t0就直接选用s了:
public float S2T(float s)
{
const int NEWTON_SEGMENT = 4;
s = Mathf.Clamp01(s);
float t = s;
// 牛顿迭代法
for (int i = 0; i < NEWTON_SEGMENT; i++)
{
t = t - (T2S(t) - s) / T2SDer(t);
}
return t;
}
结果:
《微积分的本质》【官方双语/合集】微积分的本质 - 系列合集_哔哩哔哩_bilibili
《数值分析》第2版 Timothy Sauer
如何得到贝塞尔曲线的曲线长度和 t 的近似关系? - 知乎