我有一个非常简单的例子,我需要绘制一个2级(4个控制点)的单个均匀B样条段,并且我试图在C#(https://en.wikipedia.org/wiki/De_Boor%27s_algorithm)中实现deBoor的算法,但我遇到了一个问题,没有多少阅读和研究帮助我找出正在发生的事情。
在我的例子中,我只在Point []数组中定义了4个控制点(p1,p2,p3和p4)。所以我只需要点p2和p3之间的曲线段。因此,我构建了一个没有前导和尾随结[0,1,2,3]的统一结阵列 - 基本上我可以在这种情况下使用i,但我这样做是为了坚持公式。我从维基百科构建了一个递归公式的实现:
看起来像这样:
Point deBoor(int i, int k, float t, int[] knots)
{
//i - knot span index
//k - degree
// t - time [0-knots.Length-1]
//knots - the knots array
if (k == 0) return points[knots[i]]/3f;
return ((t - knots[i]) / (knots[i + k] - knots[i])) * deBoor(i, k - 1, t, knots) + ((knots[i + k + 1] - t) / (knots[i + k + 1] - knots[i + 1])) * deBoor(i + 1, k - 1, t, knots);
}
我试着从deBtor方法中得到这样的观点:
float t = time * (points.Length - 1); //time ranges from 0 to 1
int[] knots = new int[] { 0, 1, 2, 3 };
point = deBoor(0, 2, t, knots);
不幸的是,我得到的结果是不正确的。这张图片显示了我的控制点是什么样的,我期待得到的以及我实际获得的内容:
我查看了其他实现,比如这个:https://gist.github.com/soraphis/61ee9185416ee23d0d40,它们似乎都是一样的,只是编码不同。我试图复制他们的解决方案,但结果更糟。所有这些让我觉得我错过了一些非常明显的东西。
你似乎混淆了结和控制点。为了使其成功,我们需要在您的解决方案中改进一些事项。
正如@fang已经提到的,你对k==0
的解决方案很奇怪。我建议更换
if (k == 0) return points[knots[i]]/3f;
通过更接近原始公式的东西,例如
if (k==0)
{
if (t <= knots[i] && t < knots[i+1])
return 1;
else
return 0;
}
正如@fang所指出的,对于具有四个控制点的二次样条,您需要七节。你提到你想要统一结,并根据你推荐的预期图片
int[] knots = new float[] {0, 1/6, 2/6, 3/6, 4/6, 5/6, 1};
请注意,结现在介于0和1之间;这意味着t
和time
将是相同的,即,
float t = time; //time ranges from 0 to 1 and so does t
如果你坚持你的结是int
s(其中,恕我直言,导致你的困惑)使用
int[] knots = new int[] { 0, 1, 2, 3, 4, 5, 6 };
float t = time * 6; //time ranges from 0 to 1 and t from 0 to 6
注意交换顺序:t
必须是time
缩放以覆盖整个结范围。
De Boor的算法评估一个B样条,即基函数之一(有些人使用冲突的命名法,并使用单词B样条作为整个样条函数而不仅仅是基函数之一;这有时会导致混淆) 。
非正式地说,对于给定的t
,你的deBoor
函数给出了i
-th控制点的系数,这是一个标量。因此,deBoor
的返回值必须是float
或double
或类似的东西,当然不是point
。
对于每个t
,您需要对这些系数缩放的控制点求和。所以你的最终结果将是这样的
point value = deBoor(0, 2, t, knots) * points[0]
+ deBoor(1, 2, t, knots) * points[1]
+ deBoor(2, 2, t, knots) * points[2]
+ deBoor(3, 2, t, knots) * points[3];
请注意,*
表示float
和point
的乘法(您可能需要重载此类运算符)并且+
表示两个point
s的总和(同样,可能需要运算符重载)。我不是很熟悉C#,所以可能有更优雅的写作方式;例如,我邀请你使用for
循环。
如果你仍然感到困惑,我建议先熟悉Bézier曲线,然后再进行B样条曲线。可以找到相对简洁的介绍,例如here。这些图片有点90年代风格,但这些想法仍然有效,并以可理解和简洁的方式呈现。