曲面网格简化是减少曲面网格中使用的面数,同时尽可能保持整体形状、体积和边界的过程。它是细分法的反面。
这里提出的算法可以使用称为边折叠的方法简化任何有向2流形曲面,具有任意数量的连接组件,有或没有边界(边界或孔)和处理(任意 genus)。 粗略地说,该方法包括迭代地用单个顶点替换一条边,每次折叠删除2个三角形。
根据用户提供的成本函数给出的优先级,边被折叠,替换顶点的坐标由另一个用户提供的放置函数确定。当满足用户提供的停止谓词时,算法终止,例如达到所需的边数。
这里实现的算法是通用的,因为它不需要表面网格具有特定类型,而是作为 MutableFaceGraph 和 HalfedgeListGraph 概念的模型。我们给出了 Surface_mesh、Polyhedron_3 和 OpenMesh 的示例。
该设计基于策略,这意味着您可以通过传递一组策略对象来定制流程的某些方面。每个策略对象都指定了算法的一个特定方面,例如如何选择边以及放置替换顶点的地方。所有政策都有一个合理的默认值。此外,API使用所谓的named-parameters技术,该技术允许您以任何顺序仅传递相关参数,忽略那些默认值合适的参数。
实现简化算法的自由函数不仅采用表面网格和所需的停止谓词,还采用控制和监控简化过程的许多附加参数。本节简要描述了为讨论算法参数设置背景的过程。
有两种略有不同的“边缘”折叠操作。一种被称为边缘塌陷,而另一种则被称为半边缘塌陷。给定连接顶点w和v的边e,边折叠操作将e、w和v替换为新顶点r,而半边折叠操作则将v拉入w,消除e并保留w。在这两种情况下,该操作都会删除边e及其相邻的2个三角形。
该包使用半边折叠操作,该操作通过额外移除1个顶点(v)和2条边来实现,每个相邻三角形一条。它可以选择将剩余的顶点(w)移动到一个新的位置,称为放置,在这种情况下,净效果与边折叠操作中的效果相同。
自然地,由边塌陷产生的曲面网格与初始曲面网格偏离了一定的量,并且由于简化的目标是减少三角形的数量,同时尽可能地保持曲面网格的整体外观,因此有必要测量这种偏差。一些方法试图测量从初始曲面网格到完全简化曲面网格的总偏差,例如,通过跟踪累积误差,同时保持简化变化的历史。其他方法,如本包中实施的方法,试图仅测量每个单独边缘塌陷的成本(由单个简化步骤引入的局部偏差),并将整个过程规划为增加成本的一系列步骤。
全局误差跟踪方法产生了高度精确的简化,但占用了大量额外的空间。成本驱动的方法,比如这个包中的方法,产生的简化稍微不那么准确,但占用的额外空间要小得多,在某些情况下甚至没有。
此包提供了两种成本驱动的方法。该软件包中实现的第一种成本驱动方法,即Lindstrom&Turk。该包中实现的第二种成本驱动方法,即Garland和Heckbert,并对Trettner和Kobbelt进行了增强。
算法分两个阶段进行。在称为收集阶段的第一个阶段中,将为曲面网格中的每条边指定初始塌陷成本。然后在第二阶段,称为折叠阶段,按照增加成本的顺序处理边缘。有些处理过的边会塌陷,而有些则只是丢弃。折叠边由顶点替换,并且重新计算现在出现在替换顶点上的所有边的折叠成本,从而影响剩余未处理边的顺序。
并非所有选择进行处理的边都会折叠。如果处理过的边不满足某些拓扑和几何条件,则可以立即丢弃,而不会塌陷。
论文中提出的算法通过将某些顶点对视为形成伪边,并对边和伪边进行折叠,来收缩(折叠)任意顶点对,而不仅仅是边。然而,收缩任意顶点对可能会导致非流形曲面网格,但该包的当前状态只能处理流形曲面网格。因此,它只能折叠边。也就是说,这个包不能用作顶点收缩的框架。因此,我们只会折叠边缘。
计算折叠成本和顶点位置的具体方式称为成本策略。用户可以选择不同的策略,以策略和相关参数的形式传递给算法。
当前版本的一揽子计划提供了一套实施三种战略的政策:默认的Lindstrom-Turk战略、Garland Heckbert系列战略,以及由可选中点位置的边长成本组成的战略(速度快得多,但不太准确)。
简化的表面网格在每一步都不会与原始表面网格(或前一步的表面网格)进行比较,因此不需要保留额外的信息,如原始表面网格或局部变化的历史。因此名称被简化为无记忆。
在每个步骤中,所有剩余的边都是用于折叠的潜在候选边,并且选择具有最低成本的边。折叠边的成本由替换边的顶点所选的位置决定。
替换顶点位置被计算为3个线性独立线性等式约束的系统的解。每个约束是通过最小化受先前计算的约束的二次目标函数来获得的。有几种可能的候选约束,每种约束都按重要性顺序考虑。候选约束可能与先前接受的约束不兼容,在这种情况下,它将被拒绝,并考虑下一个约束。一旦接受了3个约束,系统就会针对顶点位置进行求解。考虑的第一个约束将保留曲面网格边界的形状(如果边轮廓具有边界边)。接下来的约束将保留曲面网格的总体积。如果需要,下一个约束将优化体积和边界形状的局部更改。最后,如果仍然需要约束(因为之前计算的约束不兼容),则会添加第三个(也是最后一个)约束,以支持等边三角形而不是细长三角形。
然后,成本是形状、体积和边界优化项的加权和,其中用户指定每个项的单位加权单位因子。
在边即将折叠时,即在所有先前折叠之后,仅使用当前与其相邻的三角形来独立计算每条边的局部更改。因此,最小局部变化的传递路径最终会产生一个相当接近绝对最小值的全局变化。
与Lindstrom-Turk策略的情况一样,引入的Garland Heckbert策略不会将生成的网格与原始网格进行比较,也不依赖于局部变化的历史。相反,它通过使用分配给每个顶点的二次矩阵来编码到原始网格的近似距离。
在其经典版本中,二次矩阵Q被分配给每个顶点v,并编码任何点p到v的相邻面的总平方距离,该总平方距离由矩阵乘积p′Qp给出。在每个步骤中,都会为折叠操作选择使折叠成本最小化的边。折叠边的成本是通过最小化误差函数p′Qp来计算的,其中Q是边端点的组合二次矩阵,p是最小化成本的点(即决策变量)。使目标函数最小化的点p被拾取为新的放置点。由于误差函数是二次函数,因此可以通过简单地计算梯度并将其等于零来找到其最小值。如果由于奇异性而失败,则在边缘上找到最优点和成本。放置新顶点后,通过简单地将已折叠的边的两端的二次矩阵求和,将新的二次阵分配给新顶点。将垂直于相邻面的附加伪面添加到边界顶点的二次矩阵中,以便尽可能多地保留网格的清晰边界。
Trettner和Kobbelt提出了概率二次曲面的概念,对Garland Heckbert进行了扩展。在这种新方法中,不再像经典版本中那样使用到输入平面或多边形的距离来执行能量最小化;相反,通过在输入中引入高斯噪声(顶点位置和面法线),使输入几何体不确定。这种变化自然会恶化结果的紧密性,但另一方面,它能够创建更均匀的三角图,并且该方法更能容忍噪声,同时仍然保持特征敏感性。
Sappho's Head模型(最左边,34882个顶点)。从左到右,四个Garland Heckbert变体的简化输出(1745个顶点)和对称Hausdorff距离:平面(0.217912)、概率平面(0.256801)、三角形(0.268872)和概率三角形(0.490846)。
算法使用的成本策略通过三种策略选择:GetPlacement、GetCost和Filter。
调用GetPlacement策略来计算半边折叠后剩余顶点的新位置。它返回一个可选值,如果不应折叠边,则该值可能不存在。
调用GetCost策略来计算折叠边的成本。此策略使用放置来计算成本(这是一种误差度量)并确定边的顺序。
该算法维护了一个内部数据结构(可变优先级队列),允许以增加成本的顺序处理每个边缘。这样的数据结构需要一些每个边缘的附加信息,例如边缘的成本。如果每条边的附加信息的记录占用N字节的存储空间,则简化100万条边(正常大小)的表面网格需要N字节的附加存储空间的100万倍。因此,为了最小化简化曲面网格所需的额外内存量,仅将成本附加到每条边,而不附加其他成本。
但这是一种权衡:折叠的成本是放置(为剩余顶点选择的新位置)的函数,因此在为每条边调用GetCost之前,还必须调用GetPlacement来获得成本函数的放置参数。但是,这种放置是一个3D点,并没有连接到每个边缘,因为这很容易使额外的存储需求增加三倍。一方面,这大大节省了内存,但另一方面也是一种处理浪费,因为当边有效地折叠时,必须再次调用GetPlacement才能知道是否要移动剩余的顶点。
早期的原型表明,将放置连接到边缘,从而避免在边缘塌陷后对放置函数进行一次冗余调用,对总运行时间几乎没有影响。这是因为每条边的成本不仅计算一次,而且在计算过程中会发生多次变化,因此也必须多次调用放置函数。缓存放置只能避免边折叠时的最后一次调用,但不能避免由于放置(和成本)更改而需要的所有先前调用。
最后,我们解释PlacementFilter策略。虽然成本是优先级队列中使用的标量,但可能会有其他标准来决定是否应执行边缘折叠。虽然这样的标准可以很容易地集成到成本函数中,即将成本设置为无穷大,以便不被视为塌陷的候选者,但我们只在边是下一个塌陷的边时测试该边的标准。这使得在标准的计算昂贵的情况下,例如当我们检查简化的网格是否在输入网格的公差包络中时,网格简化更快。
Lindstrom-Turk成本计算和Garland-Heckbert成本计算都是用于表面网格简化的成本计算方法。它们的主要区别在于计算成本的方式和考虑的因素不同。
Lindstrom-Turk成本计算方法是一种基于三角形面积变化的比例来计算成本的方法。它通过比较简化前后三角形面积的变化来计算每个三角形的成本,并将这些成本累加起来得到整个网格的成本。这种方法考虑了三角形面积的变化,但可能无法完全反映网格表面的整体形状变化。
Garland-Heckbert成本计算方法是一种基于顶点位置变化的方法。它通过计算每个顶点在简化前后的位置变化量来计算该顶点的成本,并将这些成本累加起来得到整个网格的成本。这种方法更直接地反映了顶点位置的变化,但可能忽略了三角形面积的变化对网格表面形状的影响。
综上所述,Lindstrom-Turk成本计算和Garland-Heckbert成本计算的区别在于它们考虑的成本因素不同。Lindstrom-Turk方法更关注三角形面积的变化,而Garland-Heckbert方法更关注顶点位置的变化。在实际应用中,选择哪种方法取决于具体的应用场景和需求。
由于该算法不存在鲁棒性问题,因此不需要精确的谓词或构造,Simple_cartesian
简化算法的实现是作为自由模板函数 Surface_mesh_simplification::edge_collapse()。该函数有两个强制参数和几个可选参数。
该算法有两个主要参数:要简化的表面网格(就地)和停止谓词。
要简化的表面网格必须是 MutableFaceGraph 和 HalfedgeListGraph 概念的模型。
在选择每个边进行处理之后,在将其分类为可折叠之前(因此是在其折叠之前),调用停止谓词。如果停止谓词返回true,则算法终止。
命名参数的概念也在BGL中引入。命名参数允许用户仅按名称指定真正需要的参数,从而使参数顺序变得不重要。
假设有一个函数f(),它有3个参数,分别叫做name、age和gender,你有变量n、a和g作为参数传递给这个函数。如果没有命名参数,你可以这样调用它:f(n,a,g),但是如果有命名参数,你可以这样调用它:f(name(n).age(a).gender(g))。
也就是说,通过将每个参数包装在一个与该参数名称匹配的函数中,给每个参数一个名称。命名参数的整个列表实际上是由点(.)分隔的函数调用的组合。因此,如果函数需要混合使用强制参数和命名参数,则使用逗号将最后一个非命名参数与第一个命名参数分隔开,如下所示:
f(非命名参数0,非命名参数1,名称(n).年龄(a).性别(g))
当你使用命名参数时,顺序是不相关的,所以:f(name(n).age(a).gender(g))等价于:f(age(a).gender(g).name(n)),你可以省略任何有默认值的命名参数。
surface_mesh:要简化的surface_msh
stop_predice:指示何时必须完成简化的策略
vertex_index_map(vimap):属性映射,为每个顶点提供唯一的整数索引
edge_index_map(eimap):属性映射,为每条边提供唯一的整数索引
edge_is_constrained_map(ebmap):指定边是否为受约束边的特性映射
get_cost(cf):计算崩溃成本的函数对象
get_placement(pf):计算剩余顶点位置的函数对象
filter(filter):函数对象,用于拒绝为下一个边折叠选择的候选对象
vis:函数对象跟踪简化过程
int r = edge_collapse(surface_mesh, stop_predicate,
CGAL::parameters::vertex_index_map(vimap)
.edge_index_map(eimap)
.edge_is_border_map(ebmap)
.get_cost(cf)
.get_placement(pf)
.filter(filter)
.visitor(vis));
曲面网格简化不能保证生成的曲面没有自相交。当使用Lindstrom-Turk方法折叠一条边时,即使是下图示的相当琐碎的网格也会导致自相交。
边v-w塌陷到顶点w之前和之后的简单网格。虽然f1和f2的法线几乎相等,但在边塌陷之后它们是相对的。
类Surface_mesh_simplification::Bounded_normal_change_filter检查放置是否会反转候选边塌陷的边的两个顶点的星形周围的面的法线。然后,它通过返回boost::none来拒绝此放置。
此筛选器类替换类Surface_mesh_simplification::Bounded_normal_change_placement的用法。使用过滤器更快,因为它只在下一个要折叠的边上执行,而不是在更新作为边折叠结果的与顶点相关的所有边的过程中执行。
表面网格简化可以通过简化的网格保持在输入网格的包络内的方式来完成。这利用了类Polyhedral_envelope,该类可以检查查询点、线段或三角形是否位于多面体包络内,多面体包络由膨胀三角形的并集组成。虽然用户给出了公差ε,但检查是保守的,也就是说,可能存在三角形,这些三角形位于作为具有半径ε的球体的闵可夫斯基和包络获得的表面内部,但位于多面体包络之外。
每个Garland Heckbert简化策略都是用一个单独的类来实现的,该类对成本和布局策略进行了重组,因为它们共享顶点二次数据,所以必须一起使用。使用基于平面的二次误差度量的经典策略是通过类Surface_mesh_simplification::GarlandHeckbert_plane_policys实现的。尽管这两个策略必须一起使用,但仍然可以使用行为修饰符(如Surface_mesh_simplification::Bounded_normal_change_placement)包装任一策略。
named-parameters技术是一种在编程中使用的参数传递方式,它允许在调用函数或方法时使用名称来指定参数,而不是使用位置或默认值。这种技术可以提高代码的可读性和可维护性,因为它允许开发者明确指定每个参数的含义和预期值。
CGAL::Surface_mesh_simplification::Count_ratio_stop_predicate
是CGAL库中的一个类,它用于在表面网格简化过程中确定何时停止简化。它基于顶点数量比例进行判断,当简化后的网格顶点数量比例低于某个阈值时,停止简化。
此类是 CGAL::Surface_mesh_default_stop_predicate
的子类,继承了其所有方法,并重写了 do_stop_processing()
方法。在这个方法中,它会比较原始和简化后的顶点数量,如果简化后的顶点数量比例低于设定的阈值,就会返回 true
,停止简化。
CGAL::Surface_mesh_simplification::edge_collapse
是表面网格简化的一种方法,也被称为“边折叠”。在这个过程中,一个边(连接两个顶点的线段)被“折叠”到一个顶点上,从而减少了网格中的顶点和面的数量。这个操作会改变网格的拓扑结构,但目的是在保持原始形状的大致特征的同时减少网格的复杂性。
在 CGAL 的 Surface_mesh_simplification
包中,edge_collapse
功能提供了多种策略和方法来控制简化的过程,包括:选择哪些边进行折叠的策略。确定边折叠的优先级或重要性的策略(例如,基于边的长度、相邻面的面积等)。保持网格的某些属性或特征的策略(例如,保持边界、保持纹理映射等)。
使用 edge_collapse
功能时,用户通常需要提供一个表面网格(通常以半边数据结构的形式)以及一系列控制简化过程的策略和参数。然后,该功能会迭代地折叠边,直到达到用户指定的停止条件(例如,达到目标顶点数、达到目标简化率等)。
CGAL::Surface_mesh_simplification::Count_stop_predicate是一个用于表面网格简化的停止条件。它是一个自定义的停止谓词,用于确定何时停止表面网格的简化过程。
停止谓词通常用于控制算法的执行,以避免过度简化或达到所需的简化程度。CGAL::Surface_mesh_simplification::Count_stop_predicate是一个计数停止谓词,它计算并比较原始表面网格和简化后表面网格的顶点数或面数。
当简化后的表面网格的顶点数或面数达到指定的阈值时,该停止谓词将停止简化过程。这有助于控制简化的程度,并保持表面网格的形状和质量。
CGAL::Surface_mesh_simplification::LindstromTurk_placement
是CGAL库中的一个类,它用于实现Lindstrom-Turk的放置算法,该算法用于在三角网格表面进行顶点放置优化。
Lindstrom-Turk的放置算法是一种基于能量的优化算法,它通过最小化能量函数来找到最优的顶点放置。能量函数通常由顶点之间的距离、法向量的一致性等因素组成。通过最小化能量函数,该算法可以有效地减少三角网格表面的误差和变形。
CGAL::Surface_mesh_simplification::LindstromTurk_placement
类提供了用于实现Lindstrom-Turk放置算法的方法和功能。它提供了初始化能量函数、计算顶点之间的相互作用力、更新顶点位置等操作的方法。此外,它还提供了用于控制算法的参数和选项,如最大迭代次数、能量阈值等。
CGAL::Surface_mesh_simplification::LindstromTurk_cost 是CGAL库中的一个类,用于计算三维表面网格简化的代价。
LindstromTurk代价函数是一种基于面和边长度的代价函数,用于衡量简化操作对表面网格的影响。该函数计算每个三角面片的面积和边长,并根据这些值来计算代价。
使用LindstromTurk代价函数进行表面网格简化时,会选择一组边,并尝试删除这些边以获得更简单的表面网格。删除边时,需要考虑边周围三角面片的变化,并计算代价函数的值。如果删除边的代价低于某个阈值,则该边可以被删除,否则该边将被保留。
CGAL::Iso_cuboid_3 是CGAL库中的一个类,它表示一个三维的等距立方体。这个类提供了许多操作和函数,用于处理和操作这个立方体。
CGAL::approximate_sqrt 是CGAL库中的一个函数,用于近似计算平方根。这个函数接受一个参数,即要计算平方根的数值,并返回一个近似的平方根值。
CGAL::Surface_mesh_simplification::Polyhedral_envelope_filter是一个用于表面网格简化的过滤器,它基于多面体包络(Polyhedral Envelope)的方法。该方法通过估计表面网格的法线方向,将网格顶点投影到一个近似的平面或曲面上,从而实现对表面网格的简化。
使用Polyhedral_envelope_filter进行表面网格简化时,首先需要选择一个目标误差阈值。该阈值用于控制简化的程度,即允许的最大顶点移除误差。然后,过滤器会计算每个顶点的法线方向,并根据法线方向将顶点投影到一个近似的平面上。在投影过程中,过滤器会评估每个顶点的投影误差,并根据误差大小决定是否移除该顶点。
Polyhedral_envelope_filter具有以下特点:保持形状:该方法能够保持原始形状的基本特征,例如边界和曲率等。局部性:该方法只考虑局部范围内的顶点信息,因此可以避免全局优化方法中的一些问题,如信息丢失和计算负担过重。可扩展性:该方法可以很容易地与其他表面网格简化方法结合使用,以实现更高级的简化操作。
总之,CGAL::Surface_mesh_simplification::Polyhedral_envelope_filter是一种有效的表面网格简化方法,适用于各种应用场景,如三维重建、网格生成、可视化等。
CGAL::Surface_mesh_simplification::Edge_collapse_visitor_base 是CGAL库中用于表面网格简化的一个基类。
表面网格简化是计算机图形学和几何计算中的一项任务,旨在通过删除网格中的一些顶点或边来减少网格的复杂性,同时保持网格的形状和特征。CGAL库提供了多种表面网格简化的算法和工具,其中Edge_collapse_visitor_base是其中一个用于定制化简化的访问器基类。
Edge_collapse_visitor_base类提供了一些基本的接口和功能,用于在边折叠过程中处理网格中的顶点和边。它允许用户在边折叠过程中执行自定义的操作,例如更新顶点的位置、处理边的连接关系、更新网格的属性等。
通过继承Edge_collapse_visitor_base类并实现其中的虚函数,用户可以定制自己的访问器类,以适应特定的简化需求。例如,在某些情况下,可能需要在边折叠后重新分配顶点的法向量,或者在边折叠后更新网格的连接关系。
typedef SMS::GarlandHeckbert_plane_policies
这是Garland-Heckbert平面策略的经典实现。Garland-Heckbert策略是用于表面网格简化的算法之一,它基于几何特征的近似估计来决定哪些边应该被折叠。
typedef SMS::GarlandHeckbert_probabilistic_plane_policies
这是Garland-Heckbert概率平面策略的实现。与经典策略相比,概率策略在选择要折叠的边时引入了随机性,从而可能提供不同的简化结果。
typedef SMS::GarlandHeckbert_triangle_policies
这是Garland-Heckbert三角形策略的经典实现。与平面策略类似,但它是专门为三角形网格设计的。
typedef SMS::GarlandHeckbert_probabilistic_triangle_policies
这是Garland-Heckbert概率三角形策略的实现。与经典三角形策略类似,但具有随机性。
CGAL::Surface_mesh_simplification::Bounded_normal_change_placement 是一个类,属于CGAL库中的表面网格简化模块。它的作用是在进行表面网格简化时,对顶点的法线进行有界调整,以保持简化后的网格与原始网格在外观上的相似性。
在表面网格简化的过程中,通常会删除一些顶点以减少网格的复杂性,但这样可能会导致网格表面的形状发生显著变化。为了减轻这种形状变化,可以调整顶点的位置或法线,以使简化后的网格与原始网格在外观上更加相似。
Bounded_normal_change_placement类提供了一种方法来执行这种法线调整。它根据给定的边界条件(例如,最大法线变化量或法线方向的变化范围)来计算新的顶点位置,以保持简化的网格与原始网格在外观上的相似性。
使用Bounded_normal_change_placement类,可以在进行表面网格简化时,通过调整顶点的法线来保持形状的相似性,从而得到更好的简化效果。