Kpure1000/USketch

B样条基函数计算问题

Closed this issue · 1 comments

B样条基函数计算问题

由基函数计算错误导致的曲线插值计算错误,直接导致曲线形状异常。

原因

通过层层Debug,我发现是Unity-C#的函数递归方式导致的问题:

与C++不同,Unity-C#对于递归的编译做了奇怪的优化,递归入栈、出栈的顺序发生了变化,而且这个区别居然还导致了函数返回结果不同。经过测试,这个和浮点数精度其实还真没关系,还就只是C#递归的问题

目前解决方案

  • 去递归
    • 使用另外一种计算方式,直接根据节点分段曲线点坐标
    • 将原有的函数去递归
  • 将函数用C++实现并封装成dll,运行时动态调用

现在采用的是第二种方法,的确可行。但是在windows下用vs生成的dll好像不能在mac里面用,所以考虑用前一种再试一下。

最终解决方案

还是没有弄清楚为什么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%左右。已经不错了。