QEF解算器给出了疯狂的结果

时间:2019-05-31 00:11:43

标签: c# unity3d voxel

当前,我正在尝试使用Unity在c#中实现双重轮廓,但是我在QEF实现方面遇到麻烦。 QEF实现对于一个简单的多维数据集就可以很好地工作,但是只要我给它一个球体,它就会完全中断。

我发现解决此问题的方法是调整在github上其他QEF求解器中找到的"PSUEDO_INVERSE_THRESHOLD"变量。但是,一旦提高此值,我就会失去所有敏锐的功能!但是,如果它太低,则任何光滑的东西(例如地形圆)都会完全破裂。我的QEF求解器在这里:

void svd_mul_matrix_vec(ref float4 result, float3x3 a, float4 b)
{
    result.x = dot(float4(a[0][0], a[0][1], a[0][2], 0.0f), b);
    result.y = dot(float4(a[1][0], a[1][1], a[1][2], 0.0f), b);
    result.z = dot(float4(a[2][0], a[2][1], a[2][2], 0.0f), b);
    result.w = 0.0f;
}

void givens_coeffs_sym(float a_pp, float a_pq, float a_qq, ref float c, ref float s)
{
    if (a_pq == 0.0f)
    {
        c = 1.0f;
        s = 0.0f;
        return;
    }
    float tau = (a_qq - a_pp) / (2.0f * a_pq);
    float stt = sqrt(1.0f + tau * tau);
    float tan = 1.0f / ((tau >= 0.0f) ? (tau + stt) : (tau - stt));
    c = rsqrt(1.0f + tan * tan);
    s = tan * (c);
}

void svd_rotate_xy(ref float x, ref float y, float c, float s)
{
    float u = x; float v = y;
    x = c * u - s * v;
    y = s * u + c * v;
}

void svd_rotateq_xy(ref float x, ref float y, ref float a, float c, float s)
{
    float cc = c * c; float ss = s * s;
    float mx = 2.0f * c * s * (a);
    float u = x; float v = y;
    x = cc * u - mx + ss * v;
    y = ss * u + mx + cc * v;
}

void svd_rotate(ref float3x3 vtav, ref float3x3 v, int a, int b)
{
    if (vtav[a][b] == 0.0) return;

    float c = 0, s = 0;
    givens_coeffs_sym(vtav[a][a], vtav[a][b], vtav[b][b], ref c, ref s);

    float x, y, z;
    x = vtav[a][a]; y = vtav[b][b]; z = vtav[a][b];
    svd_rotateq_xy(ref x, ref y, ref z, c, s);
    vtav[a][a] = x; vtav[b][b] = y; vtav[a][b] = z;

    x = vtav[0][3 - b]; y = vtav[1 - a][2];
    svd_rotate_xy(ref x, ref y, c, s);
    vtav[0][3 - b] = x; vtav[1 - a][2] = y;

    vtav[a][b] = 0.0f;

    x = v[0][a]; y = v[0][b];
    svd_rotate_xy(ref x, ref y, c, s);
    v[0][a] = x; v[0][b] = y;

    x = v[1][a]; y = v[1][b];
    svd_rotate_xy(ref x, ref y, c, s);
    v[1][a] = x; v[1][b] = y;

    x = v[2][a]; y = v[2][b];
    svd_rotate_xy(ref x, ref y, c, s);
    v[2][a] = x; v[2][b] = y;
}

void svd_solve_sym(float3x3 a, ref float4 sigma, float3x3 v)
{
    // assuming that A is symmetric: can optimize all operations for 
    // the upper right triagonal
    float3x3 vtav = 0;
    vtav[0][0] = a.c0.x; vtav[0][1] = a.c1.x; vtav[0][2] = a.c2.x;
    vtav[1][0] = 0.0f; vtav[1][1] = a.c1.y; vtav[1][2] = a.c2.y;
    vtav[2][0] = 0.0f; vtav[2][1] = 0.0f; vtav[2][2] = a.c2.z;

    // assuming V is identity: you can also pass a matrix the rotations
    // should be applied to. (U is not computed)
    for (int i = 0; i < 5; ++i)
    {
        svd_rotate(ref vtav, ref v, 0, 1);
        svd_rotate(ref vtav, ref v, 0, 2);
        svd_rotate(ref vtav, ref v, 1, 2);
    }

    sigma = float4(vtav[0][0], vtav[1][1], vtav[2][2], 0.0f);
}

float svd_invdet(float x, float tol)
{
    return (abs(x) < tol || abs(1.0f / x) < tol) ? 0.0f : (1.0f / x);
}

void svd_pseudoinverse(ref float3x3 o, float4 sigma, float3x3 v)
{
    float d0 = svd_invdet(sigma.x, 10000f);
    float d1 = svd_invdet(sigma.y, 10000f);
    float d2 = svd_invdet(sigma.z, 10000f);

    o[0][0] = v[0][0] * d0 * v[0][0] + v[0][1] * d1 * v[0][1] + v[0][2] * d2 * v[0][2];
    o[0][1] = v[0][0] * d0 * v[1][0] + v[0][1] * d1 * v[1][1] + v[0][2] * d2 * v[1][2];
    o[0][2] = v[0][0] * d0 * v[2][0] + v[0][1] * d1 * v[2][1] + v[0][2] * d2 * v[2][2];
    o[1][0] = v[1][0] * d0 * v[0][0] + v[1][1] * d1 * v[0][1] + v[1][2] * d2 * v[0][2];
    o[1][1] = v[1][0] * d0 * v[1][0] + v[1][1] * d1 * v[1][1] + v[1][2] * d2 * v[1][2];
    o[1][2] = v[1][0] * d0 * v[2][0] + v[1][1] * d1 * v[2][1] + v[1][2] * d2 * v[2][2];
    o[2][0] = v[2][0] * d0 * v[0][0] + v[2][1] * d1 * v[0][1] + v[2][2] * d2 * v[0][2];
    o[2][1] = v[2][0] * d0 * v[1][0] + v[2][1] * d1 * v[1][1] + v[2][2] * d2 * v[1][2];
    o[2][2] = v[2][0] * d0 * v[2][0] + v[2][1] * d1 * v[2][1] + v[2][2] * d2 * v[2][2];
}

void svd_solve_ATA_ATb(
    float3x3 ATA,
    float4 ATb,
    ref float4 x)
{
    float3x3 V = 0;
    V[0][0] = 1.0f; V[0][1] = 0.0f; V[0][2] = 0.0f;
    V[1][0] = 0.0f; V[1][1] = 1.0f; V[1][2] = 0.0f;
    V[2][0] = 0.0f; V[2][1] = 0.0f; V[2][2] = 1.0f;

    float4 sigma = 0;
    svd_solve_sym(ATA, ref sigma, V);

    // A = UEV^T; U = A / (E*V^T)
    float3x3 Vinv = 1;
    svd_pseudoinverse(ref Vinv, sigma, V);
    svd_mul_matrix_vec(ref x, Vinv, ATb);
}

void svd_vmul_sym(ref float4 result, float3x3 A, float4 v)
{
    float4 A_row_x = float4(A.c0.x, A.c1.x, A.c2.x,0);//{ A[0], A[1], A[2], 0.f };

    result.x = dot(A_row_x, v);
    result.y = A.c1.x * v.x + A.c1.y * v.y + A.c2.y * v.z;
    result.z = A.c2.x * v.x + A.c2.y * v.y + A.c2.z * v.z;
}

// QEF
////////////////////////////////////////////////////////////////////////////////

void qef_add(
    float4 n, float4 p,
    ref float3x3 ATA,
    ref float4 ATb,
    ref float4 pointaccum)
{
    ATA.c0.x += n.x * n.x;
    ATA.c1.x += n.x * n.y;
    ATA.c2.x += n.x * n.z;
    ATA.c1.y += n.y * n.y;
    ATA.c2.y += n.y * n.z;
    ATA.c2.z += n.z * n.z;

    float b = dot(p, n);
    ATb.x += n.x * b;
    ATb.y += n.y * b;
    ATb.z += n.z * b;

    pointaccum.x += p.x;
    pointaccum.y += p.y;
    pointaccum.z += p.z;
    pointaccum.w += 1.0f;
}

float qef_calc_error(float3x3 A, float4 x, float4 b)
{
    float4 tmp = 0;

    svd_vmul_sym(ref tmp, A, x);
    tmp = b - tmp;

    return dot(tmp, tmp);
}

float qef_solve(
    float3x3 ATA,
    float4 ATb,
    float4 pointaccum,
    ref float4 x)
{
    float4 masspoint = pointaccum / pointaccum.w;

    float4 A_mp = 0;
    svd_vmul_sym(ref A_mp, ATA, masspoint);
    A_mp = ATb - A_mp;

    svd_solve_ATA_ATb(ATA, A_mp, ref x);

    float error = qef_calc_error(ATA, x, ATb);
    x += masspoint;

    return error;
}

float4 qef_solve_from_points(

NativeSlice<float4> positions,

NativeSlice<float4> normals,

int count,

ref float error)
{

float4 pointaccum = 0;
float4 ATb = 0;
float3x3 ATA = 0;

for (int i= 0; i<count; ++i) {
    qef_add(normals[i], positions[i], ref ATA, ref ATb, ref pointaccum);
}

float4 solved_position = 0;

error = qef_solve(ATA, ATb, pointaccum, ref solved_position);
return solved_position;
}

高阈值保持: enter image description here

低阈值保持: enter image description here

0 个答案:

没有答案