从数组中查找重复的 3D 顶点

Posted

技术标签:

【中文标题】从数组中查找重复的 3D 顶点【英文标题】:Find duplicate 3D vertices from array 【发布时间】:2015-12-10 14:00:24 【问题描述】:

我有一个点数组。每个点都有 position(x, y, z) 和法向量 (xn, yn, zn) ,总共 6 个双精度数。我需要在这个数组中找到唯一的元素,并考虑浮点容差删除重复的条目。

什么是实现它的简单有效的方法? 我考虑过构建一些空间划分结构,比如 BSP 或 KD-Tree。但我认为应该有更优化的方式,比如智能哈希字典或其他方式。

所以我正在寻求一条建议,有没有已经实现它的轻量级 C++ 库?

【问题讨论】:

std::set 怎么样? @ZDF: "考虑浮点容差"... "考虑浮点容差":这使得结果不唯一,我的意思是 x-epsilonx 是等价的,所以删除其中一个,但是使用 x + epsilon,我们有xx-epsilon, x+epsilon 作为可能的输出。 @Jarod42 定义您自己的比较。 是的,您可以使用std::set 并为其定义自定义比较函数。 【参考方案1】:

最简单的方法是四舍五入到最近的 epsilon 并将点带入整数范围(将所有值乘以 1/epsilon)。一旦它们是整数哈希就可以正常工作(std::unordered_setstd::unordered_map)。这可能会错过一些情况,其中 2 个点很接近,但它们的舍入方式不同。您可以通过舍入两种方式并考虑与任一结果发生冲突来克服这个问题。

如果您使用std::set/std::map,请注意它们具有 log(N) 访问复杂性(相对于哈希版本的常量)。在这一点上,您同样可以使用 BSP 或 KD-Tree(只要您已经有一些库已经实现了它们)。

【讨论】:

是的,我认为这将是一个很好的解决方案,但由于我有 6 个坐标和 6 个可能不同的回合结果,我需要对每个点进行 2^6=64 哈希测试。但是为不规则分布的点构建 BSP 树,我认为会更长 您不需要对所有 6 个参数进行四舍五入。我只会为这个位置做(2^3 = 8 个哈希测试)。您不需要合并找到的所有点,因此您可以在找到相邻点后比较法线,然后决定是否合并它们。【参考方案2】:

我的实现:

class VertexMap 
public:
  VertexMap(double tolerance): m_tolerance(tolerance), m_invTolerance(1 / tolerance), m_offset(m_tolerance * 0.1) 
    m_offset = m_tolerance * 0.1;
    m_offset2 = m_offset * 2.0;
  
  void add(const MbFloatPoint3D &pos, const MbFloatVector3D &normal, const MbFloatPoint &texture, size_t index) 
    m_vertices.emplace(pos, normal, texture, index, m_invTolerance);
  

  size_t findIndex(const MbFloatPoint3D &pos, const MbFloatVector3D &normal, const MbFloatPoint &texture) 
    auto vertex = Vertex(pos, normal, texture, 0, m_invTolerance);
    auto it = m_vertices.find(vertex);
    auto itEnd = m_vertices.end();
    if (it == itEnd) 
      vertex.pos.x -= m_offset;
      vertex.pos.y -= m_offset;
      vertex.pos.z -= m_offset;
      it = m_vertices.find(vertex); // (---)
      if (it == itEnd) 
        vertex.pos.x += m_offset2;
        it = m_vertices.find(vertex); // (+--)
        if (it != itEnd) 
          vertex.pos.y += m_offset2;
          it = m_vertices.find(vertex); // (++-)
          if (it != itEnd) 
            vertex.pos.x -= m_offset2;
            it = m_vertices.find(vertex); // (-+-)
            if (it != itEnd) 
              vertex.pos.z += m_offset2;
              it = m_vertices.find(vertex); // (-++)
              if (it != itEnd) 
                vertex.pos.y -= m_offset2;
                it = m_vertices.find(vertex); // (--+)
                if (it != itEnd) 
                  vertex.pos.x += m_offset2;
                  it = m_vertices.find(vertex); // (+-+)
                  if (it != itEnd) 
                    vertex.pos.y += m_offset2;
                    it = m_vertices.find(vertex); // (+++)
                  
                
              
            
          
        
      
    
    if (it != itEnd)
      return it->index;
    else
      return SIZE_MAX;
  

private:
  class Vertex 
  public:
    Vertex(const MbFloatPoint3D &pos, const MbFloatVector3D &normal, const MbFloatPoint &texture, size_t index, double invTolerance):
        pos(pos), normal(normal),texture(texture), index(index) 
      normalizedx = pos.x * invTolerance;
      normalizedy = pos.y * invTolerance;
      normalizedz = pos.z * invTolerance;
    
    MbFloatPoint3D pos;
    MbFloatVector3D normal;
    MbFloatPoint texture;
    size_t index;
    int64_t normalizedx;
    int64_t normalizedy;
    int64_t normalizedz;

    bool operator==(const Vertex &other) const 
      return Equalsd(pos, other.pos) && Equalsd(normal, other.normal) && Equalsd(texture, other.texture);
    
  ;

  struct VertexHasher
  
      size_t operator()(const Vertex& k) const
      
        size_t h1 = std::hash<int64_t>()(k.normalizedx);
        size_t h2 = std::hash<int64_t>()(k.normalizedy);
        size_t h3 = std::hash<int64_t>()(k.normalizedz);
        return (h1 ^ (h2 << 1)) ^ h3;
      
  ;

  double m_tolerance;
  double m_invTolerance;
  double m_offset;
  double m_offset2;
  std::unordered_set<Vertex, VertexHasher> m_vertices;
;

【讨论】:

以上是关于从数组中查找重复的 3D 顶点的主要内容,如果未能解决你的问题,请参考以下文章

从顶点数组生成三角形网格的算法

如何从 Direct3d11 中的顶点缓冲区读取顶点

GL_BLEND 不能与顶点数组一起正常工作

使用广度优先搜素查找路径

3D Computer Grapihcs Using OpenGL - 19 Vertex Array Object(顶点数组对象)

D3D11 IASetVertexBuffers 函数与顶点数据的两种组织方式