3D Surface Subdivision Methods 3D 曲面细分方法

这篇具有很好参考价值的文章主要介绍了3D Surface Subdivision Methods 3D 曲面细分方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原文地址: https://doc.cgal.org/latest/Subdivision_method_3/index.html#Chapter_3D_Surface_Subdivision_Methods

细分方法递归地细化控制网格并生成逼近极限表面的点。 该包由四种流行的细分方法及其细化主机组成。 支持的细分方法包括 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分。 它们各自的细化宿主是 Pqq、Ptq、Dqq 和 √ 3细化。 通过替换细化主机的几何计算,可以轻松扩展这些方法的变体。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

1 介绍

细分方法是从任意多边形网格生成平滑表面的简单而强大的方法。 与基于样条的曲面(例如 NURBS)或其他基于数值的建模技术不同,细分方法的用户不需要细分方法的数学知识。 几何形状的自然直觉足以控制细分方法。

Subdivision_method_3 适用于 Polyhedron_3 和 Surface_mesh 类,因为它们是 MutableFaceGraph 概念的模型,其目标是易于使用和扩展。 Subdivision_method_3 不是一个类,而是一个命名空间,其中包含四种流行的细分方法及其细化函数。 其中包括 Catmull-Clark、Loop、Doo-Sabin 和 √ 3细分。 通过替换细化主机的几何计算,可以轻松扩展这些方法的变体。

2 细分法

在本章中,我们将解释细分方法的一些基础知识。 我们仅关注有助于您理解包设计的主题。 有关细分方法的详细信息可以在[6]中找到。 本节介绍的一些术语将在后面的章节中再次使用。 如果您只对使用特定的细分方法感兴趣, 一个快速示例:Catmull-Clark 细分提供了有关使用 Catmull-Clark 细分的快速教程。

细分方法递归地细化粗网格并生成更接近光滑表面的近似值。 粗网格可以具有任意形状,但必须是 二维流形。 在 二维 流形中,每个内部点都有一个与 2D 圆盘同胚的邻域。 非流形的细分方法已经开发出来,但在 Subdivision_method_3 中没有考虑。 该章节预告片展示了在 CAD 模型上进行 Catmull-Clark 细分的步骤。 通过四等分模式反复细化粗网格,并生成新的点以近似平滑的表面。

实践中使用了许多细化模式。 Subdivision_method_3 支持四种最流行的模式,每种模式都被 Catmull-Clark[1]、Loop[4]、Doo-Sabin[2] 和 √3 subdivision[3] 使用(下图中从左到右) 。 我们通过它们的拓扑特征而不是相关的细分方法来命名这些模式。 PQQ 表示原始四边形四边形。 PTQ 表示原三角形四等分。 DQQ 表示对偶四边形四等分。 √3表示三角剖分向细分面收敛的速度。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

该图演示了 5 价顶点/面的 1-disk上的这四种细化模式。 细化的网格显示在源网格下方。 细化网格上的点是通过对源网格上的相邻点进行平均而生成的。 称为模板的图确定其点对细化点的位置有贡献的源邻域。 细化模式通常定义多个模板。 例如,PQQ 细化有一个顶点节点模板,它定义了输入顶点的 1 环; 边缘节点模板,定义输入边缘的 1 环; 以及一个面节点模板,它定义了一个输入面。 PQQ 细化的模板如下图所示。 顶行中的蓝色邻域表示红色细化节点的相应模板。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

带有权重的模板称为几何模板。细分方法为每个模板定义一个几何掩模,并通过对掩模加权的源点进行平均来生成新的点。几何掩模是精心挑选的,以满足一定的表面光滑度和形状质量的要求。Catmull-Clark细分的几何掩模如下图所示。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

此处显示的权重未标准化,n 是顶点的价数。 生成的点(红色)是通过加权点的总和来计算的。 例如,Catmull-Clark 面节点是通过对其模板上每个点的 1/4 求和来计算的。

模板可以具有无限数量的几何蒙版。 例如,PQQ 细化的面部节点可以通过每个模板节点的 1/5 而不是 1/4 的总和来计算。 尽管在 Subdivision_method_3 中具有任何类型的几何蒙版都是合法的,但结果曲面可能是奇数、不平滑或什至不存在。 [6] 解释了为质量细分表面设计掩模的细节。

3 一个简单的例子:Catmull-Clark 细分

假设您熟悉 Surface_mesh,您可以毫不费力地将 Subdivision_method_3 集成到您的程序中。

文件 Subdivision_method_3/CatmullClark_subdivision.cpp

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/subdivision_method_3.h>
#include <CGAL/Timer.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <fstream>
typedef CGAL::Simple_cartesian<double>         Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3>    PolygonMesh;
using namespace std;
using namespace CGAL;
namespace params = CGAL::parameters;
int main(int argc, char** argv) {
  if (argc > 4) {
    cerr << "Usage: CatmullClark_subdivision [d] [filename_in] [filename_out] \n";
    cerr << "         d -- the depth of the subdivision (default: 1) \n";
    cerr << "         filename_in -- the input mesh (.off) (default: data/quint_tris.off) \n";
    cerr << "         filename_out -- the output mesh (.off) (default: result.off)" << endl;
    return 1;
  }
  int d = (argc > 1) ? boost::lexical_cast<int>(argv[1]) : 1;
  const std::string in_file = (argc > 2) ? argv[2] : CGAL::data_file_path("meshes/quint_tris.off");
  const char* out_file = (argc > 3) ? argv[3] : "result.off";
  PolygonMesh pmesh;
  std::ifstream in(in_file);
  if(in.fail()) {
    std::cerr << "Could not open input file " << in_file << std::endl;
    return 1;
  }
  in >> pmesh;
  Timer t;
  t.start();
  Subdivision_method_3::CatmullClark_subdivision(pmesh, params::number_of_iterations(d));
  std::cerr << "Done (" << t.time() << " s)" << std::endl;
  std::ofstream out(out_file);
  out << pmesh;
  return 0;
}

此示例演示了在 Surface_mesh 上使用 Catmull-Clark 细分方法。 只有一行值得详细解释:

Subdivision_method_3::CatmullClark_subdivision(pmesh, params::number_of_iterations(d));

Subdivision_method_3 指定细分函数的命名空间。 CatmullClark_subdivision(P, params::number_of_iterations(d)) 在 d 次细化迭代后计算多边形网格 pmesh 的 Catmull-Clark 细分曲面。 多边形网格 pmesh 通过引用传递,并通过细分函数进行修改(即细分)。

此示例展示如何使用 Subdivision_method_3 细分 Surface_mesh。 应用程序定义的多边形网格可以使用专门的内核和/或专门的内部容器。 应用程序定义的多边形网格与 Subdivision_method_3 一起使用有一个主要限制:内部容器中的基元(例如顶点、半边和面)是按顺序排序的(例如 std::vector 和 std::list)。 这意味着迭代器按照原语的创建/插入顺序遍历原语。

细化版主对这两个限制做了详细的解释。

4 Catmull-Clark 细化

Subdivision_method_3 旨在允许自定义细分方法。 本节介绍 Subdivision_method_3 中 Catmull-Clark 细分函数的实现。 该实现演示了将 PQQ 细化定制为 Catmull-Clark 细分。

当开发细分方法时,选择细化模式,然后开发一组几何掩模来定位新点。 实现细分方法需要三个关键组件:

  • 可以表示任意 二维流形的网格数据结构,
  • 细化网格数据结构的过程,
  • 以及计算新点的几何掩模。

E. Catmull 和 J. Clark 选择 PQQ 细化作为其细分方法,并开发了一组几何掩模来从控制网格生成(或更准确地说,近似)B 样条曲面。Subdivision_method_3提供了一个将 Catmull-Clark 细分方法的所有三个组成部分粘合在一起的函数。

template <class PolygonMesh, class Mask, class NamedParameters>
void PQQ(PolygonMesh& p, Mask mask, NamedParameters np)

PolygonMesh 必须是 Polyhedron_3、Surface_mesh 或 MutableFaceGraph 概念的任何其他模型的实例。 它是任意 二维 流形的通用网格数据结构。 PQQ() 细化控制网格 p,是一个细化主机,它使用策略类 Mask 作为其几何计算的一部分。 在细化过程中,PQQ() 通过与掩模配合来计算并分配新点。 为了实现Catmull-Clark细分,几何策略Mask必须实现Catmull-Clark细分的几何掩模。 迭代次数以及顶点图可以使用命名参数 np 来指定。

为了实现几何掩码,我们需要知道细化主机如何与其几何掩码进行通信。 PQQ 细化定义了三个模板,因此 Catmull-Clark 细分需要三个几何掩模。 以下类定义了用于 PQQ 细化的模板的接口。

template <class PolygonMesh>
class PQQMask_3 {
  void face_node(boost::graph_traits<PolygonMesh>::face_descriptor face, Point_3& pt);
  void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor edge, Point_3& pt);
  void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor vertex, Point_3& pt);
};

PQQMask_3 中的每个类函数根据图元描述符的邻域计算一个新点,并将新点分配给 Point_3& pt。

我们用 Catmull-Clark 细分的几何掩模来实现每个类函数。

template <class PolygonMesh>
class CatmullClark_mask_3 {
  typedef boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
  typedef boost::property_traits<VertexPointMap>::value_type Point_3;
  Polygonmesh pmesh;
  VertexPointMap vpm;
  CatmullClark_mask_3(PolygonMesh &pmesh, VertexPointMap vpm)
  : pmesh(pmesh, vpm(vpm)
  {}
  void face_node(face_descriptor face, Point_3& pt) {
    int n = 0;
    Point_3 p(0,0,0);
    for(halfedge_descriptor hd : halfedges_around_face(face,pmesh)){
      p = p + get(vpm, (target(hd,pmesh)) - ORIGIN);
      ++n;
    }
    pt = ORIGIN + (p - ORIGIN)/FT(n);
  }
};
  void edge_node(halfedge_descriptor edge, Point_3& pt) {
    Point_3 p1 = get(vpm,target(edge, pmesh));
    Point_3 p2 = get(vpm,source(edge, pmesh));
    Point_3 f1, f2;
    face_node(face(edge,pmesh), f1);
    face_node(face(opposite(edge,pmesh),pmesh), f2);
    pt = Point_3((p1[0]+p2[0]+f1[0]+f2[0])/4,
                 (p1[1]+p2[1]+f1[1]+f2[1])/4,
                 (p1[2]+p2[2]+f1[2]+f2[2])/4 );
  }
  void vertex_node(vertex_descriptor vertex, Point_3& pt) {
    Halfedge_around_target_circulator<PolygonMesh> vcir(vertex,pmesh);
    typename boost::graph_traits<PolygonMesh>::degree_size_type n = degree(vertex,pmesh);
    FT Q[] = {0.0, 0.0, 0.0}, R[] = {0.0, 0.0, 0.0};
    Point_3 S = get(vpm,vertex);
    Point_3 q;
    for (int i = 0; i < n; i++, ++vcir) {
      Point_3 p2 = get(vpm, target(opposite(*vcir,pmesh),pmesh));
      R[0] += (S[0]+p2[0])/2;
      R[1] += (S[1]+p2[1])/2;
      R[2] += (S[2]+p2[2])/2;
      face_node(face(*vcir,pmesh), q);
      Q[0] += q[0];
      Q[1] += q[1];
      Q[2] += q[2];
    }
    R[0] /= n;    R[1] /= n;    R[2] /= n;
    Q[0] /= n;    Q[1] /= n;    Q[2] /= n;
    pt = Point_3((Q[0] + 2*R[0] + S[0]*(n-3))/n,
                 (Q[1] + 2*R[1] + S[1]*(n-3))/n,
                 (Q[2] + 2*R[2] + S[2]*(n-3))/n );
  }
};

为了调用 Catmull-Clark 细分方法,我们使用刚刚定义的 Catmull-Clark 掩码调用 PQQ()。

PQQ(pmesh, CatmullClark_mask_3(pmesh), params::number_of_iterations(depth));

Loop、Doo-Sabin 和 √3 细分使用类似的过程来实现:选择细化主机并实施几何策略。 开发自己的细分方法的关键是实现细化主体和几何策略的正确组合。 接下来的两节将对此进行解释。

5 Refinement Host

细化主机是多边形网格类和几何掩模类的模板函数。 它细化输入的多边形网格,并通过几何蒙版计算新点。 Subdivision_method_3 支持四种细化主机:原始四边形四等分 (PQQ)、原始三角形四等分 (PTQ)、对偶四边形四等分 (DQQ) 和 √3 三角剖分。 它们分别由 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分使用。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

namespace Subdivision_method_3 {
template <class PolygonMesh, class Mask, class NamedParameters>
void PQQ(PolygonMesh& pmesh, Mask mask, NamedParameters np);
template <class PolygonMesh, class Mask, class NamedParameters>
void PTQ(PolygonMesh& pmesh, Mask mask, NamedParameters np);
template <class PolygonMesh,  class Mask, class NamedParameters>
void DQQ(PolygonMesh& pmesh, Mask mask, NamedParameters np)
template <class PolygonMesh,  class Mask, class NamedParameters>
void Sqrt3(PolygonMesh& pmesh, Mask mask, NamedParameters np)
}

网格类必须是MutableFaceGraph的模型,并且必须是三角形网格或多边形网格,掩模是实现细分方法的几何掩模的策略类。

细化主机细化输入多边形网格,维护模板(即控制网格和细化网格之间的映射),并调用几何蒙版来计算新点。 在 Subdivision_method_3 中,细化被实现为一系列连接操作(主要是欧拉操作)。 连接操作的顺序在维护模板时起着关键作用。 通过将源子网格的顺序与细化顶点相匹配,基元中不需要任何标志来注册模板。 它避免了细化主机对多边形网格类的数据依赖。 为了使排序技巧发挥作用,多边形网格类必须有一个顺序容器(例如向量或链表)作为内部存储。 顺序容器保证多边形网格的迭代器始终按照图元的插入顺序遍历图元。 非顺序结构(例如树或映射)不提供所需的排序,因此不能与 Subdivision_method_3 一起使用。

尽管 Subdivision_method_3 不需要标志来支持细化和模板,但它仍然需要知道如何计算和存储几何数据(即点)。 Subdivision_method_3 的类具有作为可选模板参数的顶点属性映射,该映射提供顶点和点之间的映射。

细化主机 PQQ 和 DQQ 在一般多边形网格上工作,PTQ 和 Sqrt3 在三角多边形网格上工作。 PTQ 和 Sqrt3 在非三角多边形网格上的结果未定义。 Subdivision_method_3在细化之前不验证网格特性的前提条件。

有关细化实现的详细信息,感兴趣的用户应参考[5]。

6 Geometry Policy

几何策略定义了一组几何掩码。 每个几何掩模都被实现为计算细分表面的新点的成员函数。

每个几何掩模接收控制网格的图元描述符(例如halfedge_descriptor),并将Point_3返回到细分的顶点。 该函数收集基元描述符的顶点邻居(即模板上的节点),并根据邻居和掩码(即模板权重)计算新点。

3D Surface Subdivision Methods 3D 曲面细分方法,cgal,3d,曲面细分

该图显示了 Catmull-Clark 细分的几何掩模。 此处显示的权重未标准化,n 是顶点的价数。 新点是通过模板上加权点的总和来计算的。 以下代码显示了面节点的几何掩模的实现。 Catmull-Clark 几何策略的完整列表位于 Catmull-Clark 细分部分。

template <class PolygonMesh, class VertexPointMap>
class CatmullClark_mask_3 {
  typedef boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
  typedef boost::property_traits<VertexPointMap>::value_type Point_3;
  CatmullClark_mask_3(PolygonMesh &pmesh, VertexPointMap vpm);
  void face_node(face_descriptor face, Point_3& pt) {
    int n = 0;
    Point_3 p(0,0,0);
    for(halfedge_descriptor hd : halfedges_around_face(face,pmesh)){
      p = p + get(vpm, (target(hd,pmesh)) - ORIGIN);
      ++n;
    }
    pt = ORIGIN + (p - ORIGIN)/FT(n);
  }
};

在此示例中,计算基于 Point_3 是 CGAL::Point_3 的假设。 这是一个假设,但不是限制。 您可以使用任何点类,只要它在多边形网格中定义为 Point_3 即可。 您可能需要修改几何策略以支持特殊点的计算和分配。 这种扩展在图形应用程序中并不罕见。 例如,您可能想要细分细分曲面的纹理坐标。

Catmull-Clark 细分的细化主机需要三个用于没有开放边界的多边形网格的几何蒙版:顶点节点蒙版、边缘节点蒙版和面节点蒙版。 为了支持具有边界的多边形网格,还需要边界节点掩码。 下面列出了 Catmull-Clark 细分的边界节点掩码,其中 ept 返回分割边的新点,vpt 返回边所指向的顶点上的新点。

void border_node(halfedge_descriptor edge, Point_3& ept, Point_3& vpt) {
  Point_3 ep1 = get(vpm, target(edge, pmesh));
  Point_3 ep2 = get(vpm, target(opposite(edge, pmesh), pmesh));
  ept = Point_3((ep1[0]+ep2[0])/2, (ep1[1]+ep2[1])/2, (ep1[2]+ep2[2])/2);
  Halfedge_around_target_circulator<Poly> vcir(edge, pmesh);
  Point_3 vp1  = get(vpm,target(opposite(*vcir, pmesh ), pmesh));
  Point_3 vp0  = get(vpm, target(*vcir, pmesh));
  --vcir;
  Point_3 vp_1 = get(vpm, target(opposite(*vcir, pmesh), pmesh));
  vpt = Point_3((vp_1[0] + 6*vp0[0] + vp1[0])/8,
                (vp_1[1] + 6*vp0[1] + vp1[1])/8,
                (vp_1[2] + 6*vp0[2] + vp1[2])/8 );
}

下面列出了所有四个细化主机的掩码接口。 DQQMask_3 没有边界节点模板,因为 DQQ 细化的细化主机不支持当前版本中的全局边界。 这可能会在未来的版本中发生变化。

template <class PolygonMesh>
class PQQMask_3 {
  void face_node(boost::graph_traits<PolygonMesh>::face_descriptor, Point_3&);
  void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&);
  void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor, Point_3&);
  void border_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&, Point_3&);
};
template <class PolygonMesh>
class PTQMask_3 {
  void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&);
  void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor, Point_3&);
  void border_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&, Point_3_&);
};
template <class PolygonMesh>
class DQQMask_3 {
public:
  void corner_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor edge, Point_3& pt);
};
template <class PolygonMesh>
class Sqrt3Mask_3 {
public:
  void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor vertex, Point_3& pt);
};

Catmull Clark mask_3、Loop_mask_3、DooSabin_mask_3 和 Sqrt3_mask_3 的源代码是学习这些模板接口的最佳来源。

7 四种细分方法

Subdivision_method_3 通过专门化各自的细化主机来支持 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分。 它们被设计用于任何 MutableFaceGraph 模型,例如 Polyhedron_3 和 Surface_mesh。 如果您的应用程序使用具有专门几何内核的多边形网格,则需要使用基于该内核的几何策略来专门化细化主机。

namespace Subdivision_method_3 {
  template <class PolygonMesh, class NamedParameters>
  void CatmullClark_subdivision(PolygonMesh& pmesh, NamedParameters np) {
    PQQ(pmesh, CatmullClark_mask_3<PolygonMesh>(pmesh), np);
  }
  template <class PolygonMesh, class NamedParameters>
  void Loop_subdivision(PolygonMesh& pmesh, NamedParameters np) {
    PTQ(pmesh, Loop_mask_3<PolygonMesh>(pmesh) , np);
  }
  template <class PolygonMesh, class NamedParameters>
  void DooSabin_subdivision(PolygonMesh& pmesh, NamedParameters np) {
    DQQ(pmesh, DooSabin_mask_3<PolygonMesh>(pmesh), np);
  }
  template <class PolygonMesh, class NamedParameters>
  void Sqrt3_subdivision(PolygonMesh& pmesh, NamedParameters np) {
    Sqrt3(pmesh, Sqrt3_mask_3<PolygonMesh>(pmesh), np);
  }
}

8 示例:自定义细分方法

Subdivision_method_3 支持在 Polyhedron_3 上使用具有笛卡尔坐标的点的四种实用细分方法。 通过具有自定义几何掩模的细化主机的专业化,可以支持更多细分方法。 以下示例开发了一种细分方法,生成改进的循环细分曲面。

文件 Subdivision_method_3/Customized_subdivision.cpp

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/subdivision_method_3.h>
#include <CGAL/Timer.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <fstream>
typedef CGAL::Simple_cartesian<double>      Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> PolygonMesh;
using namespace std;
using namespace CGAL;
namespace params = CGAL::parameters;
// ======================================================================
template <class Poly>
class WLoop_mask_3 {
  typedef Poly                                         PolygonMesh;
  typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor   vertex_descriptor;
  typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
  typedef typename boost::property_map<PolygonMesh, vertex_point_t>::type Vertex_pmap;
  typedef typename boost::property_traits<Vertex_pmap>::value_type Point;
  typedef typename boost::property_traits<Vertex_pmap>::reference Point_ref;
  PolygonMesh& pmesh;
  Vertex_pmap vpm;
public:
  WLoop_mask_3(PolygonMesh& pmesh)
    : pmesh(pmesh), vpm(get(CGAL::vertex_point, pmesh))
  {}
  void edge_node(halfedge_descriptor hd, Point& pt) {
    Point_ref p1 = get(vpm, target(hd,pmesh));
    Point_ref p2 = get(vpm, target(opposite(hd,pmesh),pmesh));
    Point_ref f1 = get(vpm, target(next(hd,pmesh),pmesh));
    Point_ref f2 = get(vpm, target(next(opposite(hd,pmesh),pmesh),pmesh));
    pt = Point((3*(p1[0]+p2[0])+f1[0]+f2[0])/8,
               (3*(p1[1]+p2[1])+f1[1]+f2[1])/8,
               (3*(p1[2]+p2[2])+f1[2]+f2[2])/8 );
  }
  void vertex_node(vertex_descriptor vd, Point& pt) {
    double R[] = {0.0, 0.0, 0.0};
    Point_ref S = get(vpm,vd);
    std::size_t n = 0;
    for(halfedge_descriptor hd : halfedges_around_target(vd, pmesh)){
      ++n;
      Point_ref p = get(vpm, target(opposite(hd,pmesh),pmesh));
      R[0] += p[0];         R[1] += p[1];         R[2] += p[2];
    }
    if (n == 6) {
      pt = Point((10*S[0]+R[0])/16, (10*S[1]+R[1])/16, (10*S[2]+R[2])/16);
    } else if (n == 3) {
      double B = (5.0/8.0 - std::sqrt(3+2*std::cos(6.283/n))/64.0)/n;
      double A = 1-n*B;
      pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
    } else {
      double B = 3.0/8.0/n;
      double A = 1-n*B;
      pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
    }
  }
  void border_node(halfedge_descriptor hd, Point& ept, Point& vpt) {
    Point_ref ep1 = get(vpm, target(hd,pmesh));
    Point_ref ep2 = get(vpm, target(opposite(hd,pmesh),pmesh));
    ept = Point((ep1[0]+ep2[0])/2, (ep1[1]+ep2[1])/2, (ep1[2]+ep2[2])/2);
    Halfedge_around_target_circulator<Poly> vcir(hd,pmesh);
    Point_ref vp1  = get(vpm, target(opposite(*vcir,pmesh),pmesh));
    Point_ref vp0  = get(vpm, target(*vcir,pmesh));
    --vcir;
    Point_ref vp_1 = get(vpm,target(opposite(*vcir,pmesh),pmesh));
    vpt = Point((vp_1[0] + 6*vp0[0] + vp1[0])/8,
                (vp_1[1] + 6*vp0[1] + vp1[1])/8,
                (vp_1[2] + 6*vp0[2] + vp1[2])/8 );
  }
};
int main(int argc, char **argv) {
  if (argc > 4) {
    cerr << "Usage: Customized_subdivision [d] [filename_in] [filename_out] \n";
    cerr << "         d -- the depth of the subdivision (default: 1) \n";
    cerr << "         filename_in -- the input mesh (.off) (default: data/quint_tris.off) \n";
    cerr << "         filename_out -- the output mesh (.off) (default: result.off)" << endl;
    return 1;
  }
  int d = (argc > 1) ? boost::lexical_cast<int>(argv[1]) : 1;
  const std::string in_file = (argc > 2) ? argv[2] : CGAL::data_file_path("meshes/quint_tris.off");
  const char* out_file = (argc > 3) ? argv[3] : "result.off";
  PolygonMesh pmesh;
  std::ifstream in(in_file);
  if(in.fail()) {
    std::cerr << "Could not open input file " << in_file << std::endl;
    return 1;
  }
  in >> pmesh;
  Timer t;
  t.start();
  Subdivision_method_3::PTQ(pmesh, WLoop_mask_3<PolygonMesh>(pmesh), params::number_of_iterations(d));
  std::cerr << "Done (" << t.time() << " s)" << std::endl;
  std::ofstream out(out_file);
  out << pmesh;
  return 0;
}

几何掩模生成的点在语义上需要收敛到光滑的表面。 这是细分曲面理论提出的要求。 Subdivision_method_3 不强制执行此要求,也不会验证细分网格的平滑度。 Subdivision_method_3保证了细分网格的拓扑属性。 属-n 2-流形确保被细分为属-n 2-流形。 但是,当专门处理设计不当的几何蒙版时,Subdivision_method_3 可能会生成奇怪的、不平滑的或什至不存在的表面。

9 实施历史

该软件包最初由 Le-Jeng Andy Shiue 开发。 对于 CGAL 4.11,它被 Andreas Fabri 和 Mael Rouxel-Labbé 推广到适用于任何 MutableFaceGraph 模型。文章来源地址https://www.toymoban.com/news/detail-804649.html

到了这里,关于3D Surface Subdivision Methods 3D 曲面细分方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CGAL的3D Alpha Shapes

            假设我们给定一个二维或三维的点集S,我们希望得到类似“这些点形成的形状”的东西。这是一个相当模糊的概念,可能有许多可能的解释,阿尔法形状就是其中之一。阿尔法形状可用于从密集的无组织数据点集进行形状重建。事实上,阿尔法形状是由一个边界

    2024年02月01日
    浏览(80)
  • CGAL笔记之网格生成——3D 表面网格生成

    这个包提供了一个函数模板来计算一个近似于表面的三角形网格。 网格划分算法需要仅通过 oracle 了解要划分网格的表面,该 oracle 能够判断给定线段、线或射线是否与表面相交,并计算交点(如果有)。此功能使包足够通用,可以应用于各种情况。例如,它可用于对描述为

    2024年02月10日
    浏览(49)
  • CGAL的3D多面体的Minkowski和

    一把勺子和一颗星星的闵可夫斯基总和。           机器人能进入房间吗?倒立机器人和障碍物的Minkowski和描述了机器人相对于障碍物的非法位置。由于Minkowski总和的边界描述了合法位置,因此机器人在外部区域和房间之间有一条路径。          Minkowski和在几何学中

    2024年02月19日
    浏览(37)
  • CGAL-2D和3D线性几何内核-点和向量-内核扩展

    计算几何算法库(CGAL)是用c++编写的,由三个主要部分组成。第一部分是内核,它由固定大小的不可修改的几何原语对象和对这些原语对象的操作组成。这些对象既表示为独立的类(由表示类参数化,表示类指定用于计算的底层数字类型),也表示为内核类的成员(允许内核具有更

    2024年02月14日
    浏览(44)
  • Qt使用qwtplot3d绘制3D曲面

    qwtplot3d 是一个基于 Qt/OpenGL 的 C++ 程序库,为开发者提供了一些3D组件。其核心通过调用 openGL 库来完成,可以实现三维曲面绘制,三维状态下折线,散点等图形的绘制。最新的版本为 qwtplot3d-0.2.7,更新于 2007-06-25,已经有十几年没更新了。   用 Qt 打开工程之后,需要先在

    2024年02月07日
    浏览(54)
  • Python绘制3D曲面图

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在数据可视化的世界中,3D曲面图是一种强大的工具,能够将复杂的数据模式以清晰直观的方式展现出来。Python提供了多种库和工具,使得创

    2024年04月28日
    浏览(38)
  • 用 MATLAB 绘制 3D 数据的曲面图

    用 MATLAB 绘制 3D 数据的曲面图 在数据可视化中,曲面图是一种很有用的图形,可以帮助我们更好地了解数据之间的关系。MATLAB 中提供了非常简单的方式来绘制 3D 数据的曲面图。 最基本的曲面图由数据表示的一个网格组成,每个点的高度对应于该点处的数据值。在 MATLAB 中,

    2024年02月10日
    浏览(35)
  • Open3D Surface reconstruction 表面重建

    在许多情况下,我们希望生成密集的3D几何体,即三角形网格(triangle mesh)。然而,从多视点立体方法或深度传感器中,我们只能获得非结构化的点云。要从此非结构化输入中获取三角形网格,我们需要执行表面重建。在文献中存在几种方法,Open3D目前实现了以下方法: Alpha

    2023年04月27日
    浏览(35)
  • 【3维视觉】一文带你学习网格细分Mesh Subdivision算法(Loop, Butterfly, Modified Butterfly, Catmull-Clark, Doo-Sabin)

    介绍了Loop, Butterfly, Modified Butterfly, Catmull-Clark, Doo-Sabin等网格细分算法。 网格超分技术,换言之曲面细分,是指将一个模型的面合理的分成更多小的面,从而提升模型精度,提高渲染效果。经典的插值超分方法是通过一个组合更新(分裂面、添加顶点和/或插入边)和一个基于相

    2024年02月03日
    浏览(35)
  • LeetCode 892. Surface Area of 3D Shapes【数组,数学】简单

    本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,

    2024年02月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包