首页 > 解决方案 > 是否有一个库/代码被剪断,它可以将网格转换为向量表示中的 sdf 以实现亚体素精确表示?

问题描述

我需要从二维网格在网格上生成一个 sdf,以将网格表示为煤渣中的封闭体。

我的第一种方法是使用距离函数(欧几里得)来检查网格点是否接近网格点,然后将值设置为 - 或 +,但这会导致分辨率不佳。接下来,我尝试将距离相加以获得连续的距离场。这导致了一个被炸毁的物体。我不确定如何表示到由网格(凹面或凸面)描述的封闭对象的距离。我目前的方法在下面的代码中描述。


#include <iostream>
#include <fstream>
#include <string>
#include <Eigen/Dense>
#include <vector>
#include <algorithm>
#include <random>

using namespace std;
using namespace Eigen;
typedef Eigen::Matrix<double, 2, 1> Vector2;
typedef Eigen::Matrix<double, 3, 2> Vector32;
typedef std::vector<Vector2, Eigen::aligned_allocator<Vector2> > Vector2List;
typedef std::vector<Eigen::Vector3i, Eigen::aligned_allocator<Eigen::Vector3i> > Vector3iList;
typedef std::vector<Vector32> Vector32List;
typedef Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic> grid_t;


void f( Vector2List vertices, Vector3iList triangles)
{ // each entry of triangles describe which vertice point belongs
  // to a triangle of the mesh 
grid_t sdf  = grid_t::Zero(resolution, resolution);
for (int x = 0; x < resolution; ++x) {
        for (int y = 0; y < resolution; ++y) {
            Vector2d pos((x + 0.5) / resolution, (y + 0.5) / resolution);
            double dist = 1 / double(resolution*resolution);
            double check = 100;
            double val = 0;
for (std::vector<Vector2>::iterator mean = vertices.begin(); mean != vertices.end(); ++mean) {
        //try sdf with euclidian distance function
                check = (pos - *mean).squaredNorm();
                if (check < dist) {
                    val = -1; break;
                }
                else {
                    val = 20;
                }

            }

            val *= resolution;
            static const double epsilon = 0.01;
            if (abs(val) < epsilon) {
                val = 0;
                numberOfClamped++;
            }
            sdf(x,  y) = val; //
        }
    }
}

标签: c++graphicseigen

解决方案


似乎您对 SDF 的实际含义略有误解。所以让我从这个开始。

有符号距离函数是二维空间上的函数,它为您提供各个点到网格上最近点的距离。距离对于网格外部的点为正,对于内部的点(或相反)为负。自然,直接在网格上的点将具有零距离。我们可以将这个函数正式表示为:

sdf(x, y) = distance

这是一个连续函数,我们需要一个可以使用的离散表示。一个常见的选择是使用一个统一的网格,就像您要使用的网格一样。然后我们在网格点对 SDF 进行采样。一旦我们获得了所有网格点的距离值,我们就可以在它们之间插入 SDF 以在任何地方获得 SDF。请注意,每个样本对应于一个点而不是一个区域(例如,一个单元格)。

考虑到这一点,让我们看一下您的代码:

Vector2d pos((x + 0.5) / resolution, (y + 0.5) / resolution);

这取决于网格点索引如何映射到全局坐标。这可能是正确的。但是,它看起来好像假设样本位置位于各个单元格的中间。同样,这可能是正确的,但我认为+ 0.5应该不考虑。

for (std::vector<Vector2>::iterator mean = vertices.begin(); mean != vertices.end(); ++mean)

这是 SDF 的近似值。它计算网格的最近顶点,而不是最近(可能位于边上)。对于密集的网格,这应该没问题。如果您有粗网格,您应该迭代边缘并计算这些边缘上的最近点。

if (check < dist) {
    val = -1; break;
} else {
    val = 20;
}

我真的不知道这是什么。如上所述,SDF 的值是有符号距离。不是任意值。此外,该符号不应与网格是否靠近网格位置相对应。所以,你应该做的是:

if(check < val * val) {
    //this point is closer than the current closest point
    val = std::sqrt(check); //set to absolute distance
    if(*mean is inside the mesh)
        val *= -1; //invert the sign
}

最后,这件作品:

val *= resolution;
static const double epsilon = 0.01;
if (abs(val) < epsilon) {
    val = 0;
    numberOfClamped++;
}

再说一次,我不知道这应该做什么。把它放在一边。


推荐阅读