我正在尝试在我的程序中实现N阶的贝塞尔曲线的公式。 在我看来,我已经做了一切正确但视觉效果不正确。
这是:
红色立方体是P0,蓝色是P8。 白色立方体是构成曲线的实际点集。 橙色立方体是控制点。
我看到的是曲线末端之前有一个循环,曲线连接到最后一个(蓝色立方体)点。看起来有一个看不见的点。 另一件事是在P0和P1之间也是奇怪的事情......
有人可以帮我解决吗?
以下是我使用的代码:
private void Update()
{
controlPointsCoords = ControlPoints.Select(p => p.transform.position).ToArray();
for (int p = 0; p < PointsSet.Count; p++)
{
PointsSet[p].transform.position = CurveDegreeN
(
controlPointsCoords,
Rt(p, PointsSet.Count)
);
}
}
private Vector3 CurveDegreeN(Vector3[] pointsCoords, float u)
{
float X = 0, Y = 0, Z = 0;
float n = pointsCoords.Length - 1;
for (int i = 0; i < pointsCoords.Length; i++)
{
var coef = (Factorial(n) / (Factorial((float)i) * Factorial(n - i))) * Mathf.Pow(u, i) * Mathf.Pow(1 - u, n - i);
X += coef * pointsCoords[i].x;
Y += coef * pointsCoords[i].y;
Z += coef * pointsCoords[i].z;
}
return new Vector3(X, Y, Z);
}
private float Factorial(float n)
{
if (n == 0) return 1;
float res = 0.0f;
for (int i = 1; i < n; i++) res += (float)Math.Log(i);
return (float)Math.Exp(res);
}
private float Rt(int current, int count)
{
return ((float)current - 0) / ((float)count - 0) * (1 - 0) + 0;
}
我希望有人能清楚这一点! 提前谢谢!
更新 我将点数减少到3.结果如下:3 Points curve。 在这里可以清楚地看到计算出现问题...还有更多建议吗?
答案 0 :(得分:1)
首先简化该代码,因为这对调试来说是不可靠的。第一步:不要使用微积分,除非这样做有实际好处。使用完整二项式计算和t次方通常与插值一样快(或慢)(贝塞尔曲线简单地表示为列表缩减),但插值很容易通过简单的加法和乘法实现,而二项式计算和权力是更多的工作。所以让我们几何学评估,而不是使用微积分:
function drawCurve(coords[]):
points = []
// the higher you make "steps", the more curve points you generate:
for (s=0, steps=10; s<=steps; s++):
t = s/steps
nt = 1 - t
list[] = coords.shallowCopy()
// We now run our list reduction to get our on-curve
// point at t, using de Casteljau's algorithm:
while(list.length > 1)
for(i = 0, e = list.length; i < e; i++):
list[i] = nt * list[i] + t * list[i+1]
list.pop()
// And what's left is our on-curve point at t.
// Beauty; push and move on to the next point.
points.push(list[0])
return points
完成。通过排除二项式和幂,并且纯粹基于迭代插值(即使用de Casteljau's algorithm)实现曲线评估,在这段代码中几乎没有什么可以“做错”:代码具有很高的质量! / p>
通过使用数组[3]代替3d向量类,您可以通过明确您的坐标使代码更加高效,这样您就不必在插值步骤中依赖运算符重载或函数调用slowldowns ,所以你会得到类似的东西:
function drawCurve(coords[]):
coords = flatten(coords) // one-time convert Vector3 to flat [x,y,z] arrays
...
while(list.length > 1)
for(i = 0, e = list.length; i < e; i++):
v1 = list[i]
v2 = list[i+1]
list[i][0] = nt * v1[0] + t * v2[0] // x
list[i][1] = nt * v1[1] + t * v2[1] // y
list[i][2] = nt * v1[2] + t * v2[2] // z
list.pop()
points.push(new Vector3(list[0]))
return points
(最后的优化虽然通常不值得,但也要展开while
,根据初始for
和计数器{{来实现单个L=list.length
循环1}},其中i
递减1,L
在i
时重置为0,并在i==L
时终止
如果你绝对需要微积分(事实上并非如此),至少可以“有效地”生成二项式系数:基于Pascal's triangle生成它们非常简单,所以对于数学的热爱协处理器不使用阶乘来评估它们,它们可以通过只添加一些整数来生成:
L==1
(如果你这样做,要么确保你记住你正在编程并且数组偏移从0开始,或者在行/列位置[0]添加虚拟值,这样你就可以“直观地”调用{{1}得到4选择2而不是5选择3)
答案 1 :(得分:0)
除了这是二进制的最低效计算之外,你应该预先计算二项式序列,而不是为你绘制的每个点重新计算它们,并且你可以避免大多数对幂函数的调用通过采用类似霍纳的方法,......
您的代码似乎是正确的,并且视觉与控制点一致,迫使中间部分成直线。高阶多项式插值可以在样本或控制点具有(相对)大的值变化的段处具有一些复杂的摆动。
更新:我一开始并没有仔细查看辅助函数,因为我不会使用因子的单独计算来计算二项式,但您的阶乘函数不包含因子n
对于带参数n
的调用,即它计算(n-1)!
。