在三维网格处理流程里,减面(mesh simplification)是降低渲染与存储开销的常用手段。但很多团队在跑完自动化减面脚本后发现,零件上的倒角、螺纹、刻字全部变圆滑,模型失去了原本的工程语义。问题并不在减面这件事本身,而在于大多数基础算法把网格当成均匀曲面去抽稀,忽略了特征边所承载的形状线索。要让减面后的模型仍然可读、可用,必须引入特征边保护机制,并结合自适应简化策略来分配不同区域的简化强度。

特征边识别与保护的基本原理
特征边通常指网格中相邻面法线夹角超过某一阈值的边,也就是我们肉眼能感知到的硬转折或折痕。在机械零件里,它可能是分模线;在建筑模型里,它可能是墙与地面的交界。如果减面算法允许对这些边做任意 collapse(边折叠),两个端点合并后新面就会跨过转折,导致棱角消失。因此第一步要用夹角测试标记特征边,例如设定阈值 30 度,凡是二面角大于它的边都进入保护集合。
保护并不只是“不碰”这么简单。在基于边折叠的框架中,如果一条边的两个端点之一连着特征边,就需要提高该边被折叠的代价,或者直接禁止折叠。更精细的做法是把特征边两端点定义为锁定顶点,任何以它们为端点的非特征边折叠都要检查是否会生成退化或跨越特征的新面。下面这段伪代码展示了用半边结构遍历并标记特征边的过程:
// 假设 HalfEdge 网格已加载,faceNormal 为面法线缓存
float feature_angle = 30.0f;
for (auto& e : mesh.edges) {
Vector3 n1 = mesh.faceNormal[e.face];
Vector3 n2 = mesh.faceNormal[e.twin->face];
float angle = acosf(dot(n1, n2));
if (angle * 180.0f / PI > feature_angle) {
e.is_feature = true;
e.vert->locked = true;
e.twin->vert->locked = true;
}
}
这种显式标记方式计算轻量,且能直接接入后续的代价函数。需要注意的是,阈值选得太小会把圆角也当特征,导致几乎不能减面;太大则漏掉弱折痕。实际项目中常结合曲率估计做自适应阈值,让平坦大面上的轻微起伏不被误判,而真正陡变的局部一定进保护集。
自适应简化策略与误差控制
统一简化率是最省事的方案:全模型一律降到百分之二十面数。但扫描件或游戏资产往往细节分布极不均匀,统一抽稀要么细节区糊掉,要么平坦区还剩太多点。自适应简化核心思想是让局部简化率跟随几何复杂度走。常用度量是每个顶点邻域的曲率或边长方差,复杂度高的地方给低简化率,简单地方给高简化率。
在 QEM(Quadric Error Metrics)框架下,自适应表现为不同区域顶点携带的二次误差矩阵不同,折叠代价自然分化。我们也可以人为在代价里乘一个区域权重:特征邻域权重调高,远场平坦区权重调低。相比之下,顶点聚类法做自适应更直观,只需在八叉树细分时让细节区格子更密,平坦区格子更大,从而天然保留特征。下面代码展示如何根据顶点曲率给折叠代价加权重:
def collapse_cost(v1, v2, base_cost, curvature):
# curvature 为该边中点邻域平均曲率,范围 0~1
region_weight = 1.0 + 5.0 * curvature
if v1.locked or v2.locked:
return float('inf')
return base_cost * region_weight
# 简化主循环示例
while mesh.face_count > target_faces:
e = pick_min_edge()
cost = collapse_cost(e.v0, e.v1, qem_cost(e), e.curvature)
if cost == float('inf'):
continue
mesh.fold(e)
自适应策略的难点是目标面数分配。若完全交给局部权重,总面数可能不达预期。工程上通常采用两遍法:先按权重算各区域配额,再在配额内独立简化。这样既能保特征,又能精确控制总量。配合特征边锁定,减面结果在视觉与功能上都可接受。
工程落地与常见误区
不少开发者在自有管线里直接调用开源减面库,然后抱怨特征丢失,其实是因为没有把识别出的特征边回传给库的约束接口。以 Blender 的 decimate 为例,它提供 sharp 边标记通道,只有把硬边先标 sharp,再开 collapse 才会避开。另一个误区是认为法线阈值越高越安全,结果把真正需要的圆角保留下来,面数降不动,反而拖慢后续烘焙。
在自研系统里,建议把特征边保护做成可配置前置 Pass,和简化解耦。这样美术可以手绘特征线,算法也能自动检测,两者取并集。同时输出简化前后特征边重合度报告,用 F1 分数衡量保留质量。下面给出一个简单的重合度统计片段:
int matched = 0;
for (auto& fe : original_feature_edges) {
if (simplified_edge_set.contains(fe.key())) {
matched++;
}
}
float recall = (float)matched / original_feature_edges.size();
从架构看,特征边保护与自适应简化不应是减面函数的两个开关,而应是网格预处理与代价函数的有机部分。把特征当作一等公民,减面就从盲目抽稀变成有意图的几何压缩,既满足实时渲染需求,也保住下游检测、装配所需的结构信息。
mesh_simplificationfeature_edgeadaptive_decimation修改时间:2026-08-18 23:24:39