9.2 曲面网格生成 (Surface_mesher)
上一节: 9.1 Mesh_3三维网格生成概述 下一节: 9.3 四面体重网格化 相关章节: 13.2 Poisson表面重建 (网格用于重建)
0. 直观理解:贴瓷砖与精密测量
0.1 生活类比:浴室贴瓷砖
想象你正在给浴室的曲面浴缸贴瓷砖。这个任务与Surface_mesher的工作非常相似:
Surface_mesher的工作方式:
- 目标:用三角形”瓷砖”覆盖复杂曲面
- 挑战:曲面弯曲处需要小瓷砖,平坦处可以大瓷砖
- 要求:瓷砖之间不能重叠,也不能留缝隙
平面贴瓷砖(简单): 曲面贴瓷砖(复杂):
┌───┬───┬───┐ ┌───┬───┐
├───┼───┼───┤ / / /
├───┼───┼───┤ / / /
└───┴───┴───┘ └───┬───┘
\ \
\ \
└───┘
均匀大小即可 需要自适应大小
(弯曲处小,平坦处大)
0.2 为什么需要曲面网格?
场景1:游戏开发
- 角色模型的3D表面表示
- 需要平衡视觉质量和渲染性能
- 少而精的三角形 = 高帧率
场景2:3D打印
- 将数字模型转换为实体对象
- 打印机需要切片数据
- 网格质量影响打印成品表面光洁度
场景3:医学手术规划
- 从CT扫描重建患者器官表面
- 医生在虚拟环境中规划手术路径
- 需要精确表示病灶区域
0.3 保护算法(Protection)的直观理解
在曲面网格生成中,保护算法就像是给曲面的重要特征”画上保护线”:
没有保护的曲面网格: 有保护的曲面网格:
╱╲ ╱╲
╱ ╲ ╱ ╲
╱ ╲ ╱ ╲
╱ ╲ ╱ ╲
╱ 尖 ╲ ╱ ● ╲
════════════ ═════╦══════
║
保护点(红色)
问题:尖锐特征被抹平 解决:特征被精确保留
9.2.1 数学理论基础
曲面网格生成问题
给定一个光滑曲面 ,曲面网格生成问题是构造一个三角形网格 ,使得:
- 几何逼近: 在几何上逼近
- 拓扑一致: 与 同胚
- 质量约束: 的三角形满足形状质量标准
Delaunay细化在曲面上的推广
Surface_mesher基于**受限Delaunay三角剖分(Restricted Delaunay Triangulation)**理论。
受限Delaunay三角剖分定义
设 是曲面 上的点集, 是 的三维Delaunay三角剖分。
受限Delaunay三角剖分 定义为:
其中Delaunay球是 的外接球(对于三角形)或外接圆(对于边)。
受限Delaunay可视化:
三维Delaunay四面体: 受限Delaunay三角形:
A A
/|\ /|\
/ | \ / | \
/ | \ / | \
B---|---C B---|---C
\ | / \ | /
\ | / \ | /
\|/ \|/
D D
四面体ABCD的 三角形ABC是受限Delaunay
外接球与曲面相交 因为它的外接球与曲面相交
拓扑保证定理
定理(Restricted Delaunay Homeomorphism):
如果采样 满足 -采样条件(),则受限Delaunay三角剖分 与曲面 同胚。
其中 -采样定义为:对于曲面 上的任意点 ,存在采样点 使得:
是 处的局部特征尺寸(Local Feature Size)。
局部特征尺寸
局部特征尺寸 定义为:
直观意义: 是 到曲面”骨架”的最小距离,反映了曲面在 处的局部复杂度。
局部特征尺寸可视化:
曲面S: Medial Axis(骨架):
╭─────╮ ┌───┐
╱ ╲ │ │
│ x │ vs │ x │ <-- lfs(x) = 到骨架的距离
│ │ │ │
╲_______╱ └───┘
在平坦区域:lfs大 在狭窄区域:lfs小
(可以稀疏采样) (需要密集采样)
保护算法(Protection)详解
对于含尖锐特征的曲面,需要保护算法确保特征被正确采样。
保护球(Protected Balls)
1D特征边保护过程:
步骤1: 识别特征边
╭─────●─────╮
╱ │ ╲
│ │ │
│ │ │
╲______│______╱
红色边 = 特征边(尖锐边)
步骤2: 在特征边上放置保护球
╭───(●)───╮
╱ │ ╲
│ (●)│(●) │
│ │ │
╲____(●)____╱
(●) = 保护球中心
步骤3: 保护球防止细化破坏特征
╭───(●)───╮
╱ / | \ ╲
│ / | \ │
│ (●) | (●) │
╲ \ | / ╱
╲(●)─┼─(●)╱
网格在保护球外生成,特征被保留
1D特征处理
对于曲面上的1D特征(如尖锐边):
- 特征检测:识别曲面的高曲率边
- 保护球放置:沿特征边放置保护球
- 约束Delaunay:保护球中心构成1D Delaunay剖分
- 受限细化:在保护球外进行曲面细化
1D特征处理可视化:
曲面边界(1D特征):
A
/ \
/ \
/ \
B C
\ /
\ /
\ /
D
保护球放置:
(A)
/ \
/ \
(B) (C)
\ /
\ /
(D)
1D Delaunay剖分:连接保护球中心
(A)
/|
/ |
(B)-(C)
\ |
\|
(D)
表面中心与Delaunay球
对于Delaunay三角形 :
表面中心(Surface Center):Delaunay球与曲面 的交点中离三角形平面最远的点。
表面半径(Surface Radius):表面中心到三角形顶点的距离。
表面中心计算:
三角形PQR: Delaunay球:
P P
/ \ /|\
/ \ / | \
/ \ / | \
Q-------R Q---|---R
\ / \ | /
\ X / \ | /
\ / \ | /
\ / \|/
S 表面中心S
X = 表面中心 = Delaunay球与曲面的交点
细化标准
Surface_mesher使用两个细化标准:
1. 尺寸标准(Size Criterion)
三角形的表面半径超过尺寸上界:
其中 是三角形的中心, 是尺寸场。
尺寸标准可视化:
尺寸太大(需要细化): 尺寸合适:
┌─────────┐ ┌───┬───┐
/ / / / /
/ / vs / / /
┌─────────┐ ├───┼───┤
| | | | |
└─────────┘ └───┴───┘
表面半径 > 尺寸上界 表面半径 ≤ 尺寸上界
2. 形状标准(Shape Criterion)
三角形的纵横比过差:
其中 是外接圆半径, 是内切圆半径。
等价于最小角条件:三角形的最小角小于阈值。
形状标准可视化:
劣质三角形(需要细化): 优质三角形:
A A
/ \ / \
/ \ / \
/ \ / \
B───────C B───────C
最小角 < 30° 最小角 ≥ 30°
(细长) (接近等边)
9.2.2 架构分析
整体架构
Surface_mesher
├── Surface(曲面定义)
│ ├── Implicit_surface_3(隐式曲面)
│ ├── Gray_level_image_3(灰度图像曲面)
│ └── Surface_mesh_complex_2_in_triangulation_3
│
├── Surface_mesh_traits(曲面特征)
│ ├── Surface_traits_base_3
│ └── Surface_mesher_traits_base_3
│
├── Mesher(网格生成器)
│ ├── Surface_mesher
│ ├── Surface_mesher_edges_level
│ └── Surface_mesher_visitor
│
└── Criteria(质量标准)
├── Surface_mesher_criteria
└── Surface_mesher_default_criteria
核心类
1. 曲面概念(Surface Concept)
class Surface_3 {
// 必需的类型
typedef ... Point_3;
typedef ... FT;
typedef ... Intersect_3; // 线段与曲面求交
typedef ... Construct_initial_points; // 构造初始点
// 必需的操作
Intersect_3 intersect_3_object();
Construct_initial_points construct_initial_points_object();
// 包围球
Sphere_3 bounding_sphere() const;
};2. 曲面特征(Surface Traits)
template <class Surface>
class Surface_mesh_traits_base_3 {
// 类型定义
typedef typename Surface::Point_3 Point_3;
typedef typename Surface::FT FT;
// 几何谓词
typedef ... Is_on_surface;
typedef ... Surface_center;
typedef ... Surface_distance;
};3. 质量标准(Criteria)
template <class Surface_mesh_traits>
class Surface_mesher_criteria_3 {
public:
// 构造函数
Surface_mesher_criteria_3(
FT angle_bound, // 最小角下界(弧度)
FT radius_bound, // 半径上界
FT distance_bound // 距离上界
);
// 判断三角形是否满足标准
bool is_bad(const Facet& f, const Tr& tr) const;
};算法流程
输入:曲面 S,质量标准 C
输出:曲面三角网格 M
1. 初始化
- 构造初始采样点集 P_0
- 构建三维Delaunay三角剖分 DT(P_0)
- 提取受限Delaunay三角剖分 DT_S(P_0)
2. 细化循环
while 存在违反质量标准的三角形 τ:
// 计算细化点
p = surface_center(τ) // 表面中心
// 插入点
P = P ∪ {p}
更新 DT(P)
更新 DT_S(P)
3. 输出网格
return DT_S(P) 的二维复形
算法可视化:
第1步:初始采样
S(曲面)
•
/ \
/ \
• •
\ /
\ /
•
第2步:识别坏三角形
S
•
/|\
/ | \
•--X--• X = 坏三角形(太大或太扁)
\ | /
\|/
•
第3步:插入表面中心
S
•
/|\
/ | \
•--●--• ● = 新插入的表面中心
/ | \
•--●--•
\ | /
\|/
•
第4步:最终网格
S
•
/|\
/ | \
•--|--•
|\ | /|
| \|/ |
•--|--•
\ | /
\|/
•
9.2.3 实现细节
1. 隐式曲面的实现
#include <CGAL/Implicit_surface_3.h>
// 定义隐式函数
typedef K::FT FT;
typedef K::Point_3 Point;
FT torus_function(const Point& p) {
FT x = p.x(), y = p.y(), z = p.z();
FT R = 2.0; // 大半径
FT r = 0.5; // 小半径
FT tmp = std::sqrt(x*x + y*y) - R;
return tmp*tmp + z*z - r*r;
}
// 创建隐式曲面
typedef CGAL::Implicit_surface_3<K, FT> Surface;
Surface surface(torus_function, // 隐式函数
Sphere_3(Point(0,0,0), 9), // 包围球
1e-5); // 精度2. 灰度图像曲面的实现
#include <CGAL/Gray_level_image_3.h>
// 从医学图像创建曲面
typedef CGAL::Gray_level_image_3<FT, Point> Gray_level_image;
Gray_level_image image("brain.inr", // 图像文件
700); // 等值面值
typedef CGAL::Implicit_surface_3<K, Gray_level_image> Surface;
Surface surface(image, Sphere_3(Point(0,0,0), 1000));3. 网格生成
#include <CGAL/make_surface_mesh.h>
#include <CGAL/Surface_mesh_complex_2_in_triangulation_3.h>
// 类型定义
typedef CGAL::Surface_mesh_triangulation_generator_3<Surface>::Type Tr;
typedef CGAL::Surface_mesh_complex_2_in_triangulation_3<Tr> C2t3;
typedef CGAL::Surface_mesher_criteria_3<Tr> Criteria;
// 创建三角剖分
Tr tr;
C2t3 c2t3(tr);
// 设置质量标准
Criteria criteria(30, // 最小角(度)
0.1, // 半径上界
0.01); // 距离上界
// 生成网格
CGAL::make_surface_mesh(c2t3, // 输出复形
surface, // 输入曲面
criteria, // 质量标准
CGAL::Manifold_with_boundary_tag()); // 拓扑标签4. 拓扑标签
CGAL提供三种拓扑标签:
// 1. 不要求流形
CGAL::Non_manifold_tag()
// 2. 要求流形(可能带边界)
CGAL::Manifold_with_boundary_tag()
// 3. 要求无边界流形
CGAL::Manifold_tag()5. 表面中心计算
表面中心是细化算法的核心:
// 计算三角形的表面中心
template <class Surface, class Tr>
Point surface_center(const Facet& f, const Surface& surface, const Tr& tr) {
// 1. 获取三角形的三个顶点
Cell_handle c = f.first;
int i = f.second;
Point p = c->vertex((i+1)%4)->point();
Point q = c->vertex((i+2)%4)->point();
Point r = c->vertex((i+3)%4)->point();
// 2. 计算外接球
Sphere circumsphere = circumsphere(p, q, r);
// 3. 求外接球与曲面的交点
// 选择离三角形平面最远的交点
std::vector<Point> intersections;
surface.intersect(circumsphere, std::back_inserter(intersections));
// 4. 返回表面中心
return select_farthest_from_plane(intersections, p, q, r);
}9.2.4 代码示例
完整示例:环面网格
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh_triangulation_generator_3.h>
#include <CGAL/Surface_mesh_complex_2_in_triangulation_3.h>
#include <CGAL/Surface_mesher_criteria_3.h>
#include <CGAL/Implicit_surface_3.h>
#include <CGAL/make_surface_mesh.h>
#include <CGAL/IO/Complex_2_in_triangulation_3_file_writer.h>
#include <fstream>
// 几何内核
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::FT FT;
typedef K::Point_3 Point;
typedef K::Sphere_3 Sphere;
// 隐式函数:环面
FT torus_function(const Point& p) {
FT x = p.x(), y = p.y(), z = p.z();
FT R = 2.0;
FT r = 0.5;
FT tmp = std::sqrt(x*x + y*y) - R;
return tmp*tmp + z*z - r*r;
}
// 曲面类型
typedef CGAL::Implicit_surface_3<K, FT> Surface;
// 三角剖分类型
typedef CGAL::Surface_mesh_triangulation_generator_3<Surface>::Type Tr;
// 二维复形类型
typedef CGAL::Surface_mesh_complex_2_in_triangulation_3<Tr> C2t3;
// 质量标准类型
typedef CGAL::Surface_mesher_criteria_3<Tr> Criteria;
int main() {
// 创建环面曲面
Surface surface(torus_function,
Sphere(Point(0, 0, 0), 9.0),
1e-5);
// 创建三角剖分和复形
Tr tr;
C2t3 c2t3(tr);
// 设置质量标准
// 角度30度,半径0.1,距离0.01
Criteria criteria(30, 0.1, 0.01);
// 生成网格
CGAL::make_surface_mesh(c2t3,
surface,
criteria,
CGAL::Manifold_tag());
// 输出统计
std::cout << "顶点数: " << tr.number_of_vertices() << std::endl;
std::cout << "面片数: " << c2t3.number_of_facets() << std::endl;
// 检查拓扑
if (c2t3.is_manifold()) {
std::cout << "网格是流形" << std::endl;
}
// 保存到文件
std::ofstream out("torus.off");
CGAL::output_surface_facets_to_off(out, c2t3);
out.close();
return 0;
}多曲面网格生成
// 定义分段隐式曲面
FT multi_surface_function(const Point& p) {
FT x = p.x(), y = p.y(), z = p.z();
// 球面
FT sphere = x*x + y*y + z*z - 1.0;
// 平面
FT plane = z - 0.5;
// 返回并集
return std::min(sphere, plane);
}
// 或者使用布尔运算
FT boolean_function(const Point& p) {
FT x = p.x(), y = p.y(), z = p.z();
FT sphere1 = x*x + y*y + z*z - 1.0;
FT sphere2 = (x-0.5)*(x-0.5) + y*y + z*z - 0.5;
// 两个球的交集
return std::max(sphere1, sphere2);
}自定义尺寸场
#include <CGAL/Surface_mesher_criteria_3.h>
// 自定义质量标准
template <class Tr>
class Adaptive_criteria {
public:
typedef typename Tr::Facet Facet;
typedef typename Tr::Geom_traits::FT FT;
// 自适应尺寸场
FT sizing_field(const Point& p) const {
FT r = std::sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
return 0.1 / (1.0 + r); // 远离原点处更粗
}
// 判断三角形是否满足标准
bool is_bad(const Facet& f, const Tr& tr) const {
// 计算三角形中心
Point center = facet_center(f, tr);
FT desired_size = sizing_field(center);
// 检查实际尺寸
FT actual_size = facet_size(f, tr);
return actual_size > desired_size;
}
};从点云重建曲面
#include <CGAL/Poisson_reconstruction_function.h>
#include <CGAL/Surface_mesh.h>
// 点云类型
typedef CGAL::Point_with_normal_3<K> Point_with_normal;
typedef std::vector<Point_with_normal> Point_list;
// 泊松重建
Point_list points;
// ... 加载点云 ...
// 构建泊松隐式函数
typedef CGAL::Poisson_reconstruction_function<K> Poisson_function;
Poisson_function function(points.begin(), points.end(),
CGAL::make_normal_of_point_with_normal_map(Point_list::value_type()));
// 计算隐式函数
function.compute_implicit_function();
// 等值面提取
typedef CGAL::Implicit_surface_3<K, Poisson_function> Surface;
Surface surface(function, bounding_sphere, 1e-5);
// 生成网格
// ... 使用 make_surface_mesh ...实际应用:医学图像处理
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh_triangulation_generator_3.h>
#include <CGAL/Surface_mesh_complex_2_in_triangulation_3.h>
#include <CGAL/Surface_mesher_criteria_3.h>
#include <CGAL/Gray_level_image_3.h>
#include <CGAL/Implicit_surface_3.h>
#include <CGAL/make_surface_mesh.h>
#include <CGAL/IO/Complex_2_in_triangulation_3_file_writer.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::FT FT;
typedef K::Point_3 Point;
int main() {
// 加载CT扫描图像
CGAL::Image_3 image;
image.read("patient_ct_scan.inr");
// 创建灰度图像曲面(等值面提取)
typedef CGAL::Gray_level_image_3<FT, Point> Gray_level_image;
Gray_level_image gray_image(image, 500); // 阈值500( Hounsfield单位)
// 定义曲面(骨骼表面)
typedef CGAL::Implicit_surface_3<K, Gray_level_image> Surface;
Surface surface(gray_image, K::Sphere_3(Point(0,0,0), 1e9));
// 三角剖分类型
typedef CGAL::Surface_mesh_triangulation_generator_3<Surface>::Type Tr;
typedef CGAL::Surface_mesh_complex_2_in_triangulation_3<Tr> C2t3;
typedef CGAL::Surface_mesher_criteria_3<Tr> Criteria;
Tr tr;
C2t3 c2t3(tr);
// 医学图像需要高精度
Criteria criteria(
30, // 最小角30度
0.5, // 半径上界0.5mm
0.1 // 距离上界0.1mm
);
// 生成网格
CGAL::make_surface_mesh(c2t3, surface, criteria, CGAL::Manifold_tag());
std::cout << "骨骼表面重建完成:" << std::endl;
std::cout << "顶点数: " << tr.number_of_vertices() << std::endl;
std::cout << "面片数: " << c2t3.number_of_facets() << std::endl;
// 保存为STL格式(用于3D打印)
std::ofstream stl_file("bone_model.stl");
CGAL::output_surface_facets_to_stl(c2t3, stl_file);
stl_file.close();
return 0;
}9.2.5 复杂度分析
时间复杂度
| 操作 | 复杂度 | 说明 |
|---|---|---|
| 初始化 | 为初始点数 | |
| 单点插入 | Delaunay更新 | |
| 表面中心计算 | 假设曲面求交为常数 | |
| 完整生成 | n为输出顶点数 | |
| 受限Delaunay提取 | m为三角形数 |
空间复杂度
- Delaunay三角剖分:
- 曲面采样:(曲面复杂度)
- 总空间:
输出规模
对于面积为 的曲面,在均匀尺寸 下:
- 顶点数:
- 三角形数:
9.2.6 理论保证
拓扑保证
定理:如果算法终止,输出网格满足:
- 同胚性:网格与输入曲面同胚
- 流形性:网格是二维流形(根据标签)
- 无自交:网格不自交
几何保证
定理:输出网格满足:
- Hausdorff距离:(由distance_bound控制)
- 法向逼近:网格法向逼近曲面法向
- 面积收敛:网格面积收敛到曲面面积
终止性
定理:对于光滑闭曲面,在适当参数下,算法保证终止。
条件:
- 曲面是光滑的()
- 尺寸场满足Lipschitz条件
- 角度界小于30度
9.2.7 与其他组件的关系
Surface_mesher
├── 依赖
│ ├── Delaunay_triangulation_3(基础三角剖分)
│ ├── Implicit_surface_3(隐式曲面表示)
│ └── AABB_tree(加速结构)
│
├── 用于
│ ├── Mesh_3(三维网格生成的前置步骤)
│ └── Surface_reconstruction(表面重建)
│
└── 相关算法
├── Poisson_reconstruction(泊松重建)
├── Advancing_front(前沿推进)
└── Marching_cubes(移动立方体)
9.2.8 应用
1. 医学图像处理
从CT/MRI数据提取等值面:
// 加载医学图像
CGAL::Image_3 image;
image.read("patient.inr");
// 创建灰度图像曲面
Gray_level_image gray_image(image, 500); // 阈值500
// 生成网格
// ...应用案例:
- 骨骼建模(骨科手术规划)
- 血管重建(血流模拟)
- 肿瘤分割(放疗规划)
- 器官打印(3D生物打印)
2. 计算机辅助设计
从隐式CAD模型生成网格:
// CSG布尔运算
FT csg_model(const Point& p) {
FT cube = std::max({std::abs(p.x()), std::abs(p.y()), std::abs(p.z())}) - 1.0;
FT sphere = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() - 1.5;
// 立方体减球体
return std::max(cube, -sphere);
}应用案例:
- 机械零件设计
- 模具制造
- 建筑构件优化
- 珠宝设计
3. 科学计算可视化
可视化标量场的等值面:
// 从模拟数据创建曲面
std::function<FT(Point)> field = [](const Point& p) {
// 模拟物理场
return simulation_value(p);
};
Surface surface(field, bounding_sphere);应用案例:
- 天气模式可视化
- 电磁场分布
- 应力分析结果
- 分子电子云
4. 参数选择指南
不同应用场景的参数建议:
| 应用 | angle_bound | radius_bound | distance_bound | 拓扑标签 |
|---|---|---|---|---|
| 医学图像 | 20-30° | 0.1-0.5 | 0.01-0.05 | Manifold_tag |
| CAD设计 | 25-30° | 0.01-0.1 | 0.001-0.01 | Manifold_tag |
| 科学可视化 | 15-25° | 0.5-2.0 | 0.05-0.2 | Manifold_with_boundary_tag |
| 游戏模型 | 20-25° | 0.1-1.0 | 0.01-0.1 | Manifold_tag |
9.2.9 总结
Surface_mesher提供了:
- 全自动曲面网格生成:从隐式定义到高质量三角形网格
- 拓扑保证:确保输出网格与输入曲面同胚
- 几何精度:通过尺寸场和距离界控制逼近精度
- 自适应细化:根据曲面曲率自动调整网格密度
- 特征保护:保留尖锐边和角等几何特征
Surface_mesher是三维重建、科学计算可视化和计算机辅助设计的关键工具,广泛应用于医学、工程、娱乐等领域。