B样条基函数计算问题
Closed this issue · 1 comments
Kpure1000 commented
B样条基函数计算问题
由基函数计算错误导致的曲线插值计算错误,直接导致曲线形状异常。
原因
通过层层Debug,我发现是Unity-C#的函数递归方式导致的问题:
与C++不同,Unity-C#对于递归的编译做了奇怪的优化,递归入栈、出栈的顺序发生了变化,而且这个区别居然还导致了函数返回结果不同。经过测试,这个和浮点数精度其实还真没关系,还就只是C#递归的问题
目前解决方案
- 去递归
- 使用另外一种计算方式,直接根据节点分段曲线点坐标
- 将原有的函数去递归
- 将函数用C++实现并封装成dll,运行时动态调用
现在采用的是第二种方法,的确可行。但是在windows下用vs生成的dll好像不能在mac里面用,所以考虑用前一种再试一下。
Kpure1000 commented
最终解决方案
还是没有弄清楚为什么Unity C#的递归会出现结果异常。
不过,我把这个算法消除递归之后,用原生C#实现了一遍,就没有问题了。(虽然感觉性能好像差不多的说?)
代码贴上来
private float deBoor_Cox(int i, int de, float u)
{
treeLineNum = pow2(de);
rk = 0;
ri = 0;
for (int it = 0; it < treeLineNum; it += 1)
{
ri = tArray[indexer(de, it)];
uArray[it] = (u >= knot[i + ri]
&& u < knot[i + ri + 1]) ? 1.0f : 0.0f;
}
rk++;
while (rk <= de)
{
for (int it = 0; it < treeLineNum; it += 2)
{
ri = tArray[indexer(de - rk, it / 2)];
div1 = knot[i + ri + rk]
- knot[i + ri];
div2 = knot[i + ri + rk + 1]
- knot[i + ri + 1];
U1 = (Mathf.Abs(div1) < 1e-3f) ? 1.0f
: (u - knot[i + ri]) / div1;
U2 = (Mathf.Abs(div2) < 1e-3f) ? 1.0f
: (knot[i + ri + rk + 1] - u) / div2;
uArray[it / 2] = U1 * uArray[it] + U2 * uArray[it + 1];
}
treeLineNum /= 2;
rk++;
}
return uArray[0];
}
其中tArray和uArray的预处理如下
tArray = new int[pow2(maxDegree) - 1];
//初始化递归树
tArray[0] = 0;
for (int i = 1; i < tArray.Length; i++)
{
tArray[i] = i % 2 != 0 ? tArray[(i - 1) / 2] : tArray[(i - 2) / 2] + 1;
}
uArray = new float[pow2(maxDegree - 1)];
经过测试,调用C++的dll方法时,20控制点、5阶的曲线,实时计算的帧率有9左右,但原生C#,同样的算法,只有8,虽然只差一帧但还是隐约看到了语言的性能差异。据说,C#的性能相当于C++的80%左右。已经不错了。