学习remeshing,今天看了知乎大佬的一篇文章,给大家共同总结学习一下,真的不错。
1. 问题
对于三角网格,可能顶点分布不均,这会影响很多网格应用的效果(如数值模拟,几何建模等),因此需要将网格均匀化。示例如下,(左)分布不均(右)均匀分布
所谓均匀的网格,最简单的度量是
- 所有边等长
- 所有三角形面积相等
- 所有顶点度数为 6
2. 算法
本文介绍的算法是 [Botsch and Kobbelt 04b][1][2] 提出的,特点是实现简单,鲁棒性好,而且效果也好。伪代码如下
remesh(target_edge_length)
low = 4/5 * target_edge_length
high = 4/3 * target_edge_length
for i = 0 to 10 do
spilt_long_edges(high)
collapse_short_edges(low, high)
equalize_valences()
tangential_relaxation()
project_to_surface()
其中target_edge_length
是网格的目标长度,可取为原网格M的平均长度 ,其中E(M)是网格的边集
2.1网格局部操作
本算法涉及三个局部操作,如下
在用半边结构时,算法实现简单但繁琐,Edge Spilt 和 Edge Flip 没什么坑,注意判断网格边界即可。Edge Collapse 比较坑,在部分情况下会不合法[3],如下(产生了二度点和重边,不再是三角网格)
Edge Collapse 不合法情形
合法时的判断条件为,其中
是边的两个端点
2.2 Spilt Long Edges
该步骤将所有长于high = 4/3 * target_edge_length
的边进行 Edge Spilt 操作,新增的点位置为边两端点的中点。伪代码如下
spilt_long_edges(high)
while exists edge e with length(e) > high do
spilt e at midpoint(e)
对于具体实现,关键在于边的遍历,特别注意 Edge Spilt 后会新增 4 条边,C++ 实现如下
unordered_set<E*> edges = mesh->Edges();
while(!edges.empty()){
auto iter = edges.begin();
E* e = *iter;
edges.erase(iter);
if(e->Length() > high){
V* v = mesh->SpiltEdge(e, e->Centroid());
for(E* adjE : v->AdjEdges()) // add adjacent edges of new vertex v
edges.push_back(adjE);
}
}
2.3 Collapse Short Edges
该步骤将所有短于low = 4/5 * target_edge_length
的边进行 Edge Collapse 操作,新增的点位置为边两端点的中点,另外要求新的边不会长于high = 4/3 * target_edge_length
。伪代码如下
collapse_short_edges(low, high)
while exists unchecked edge e with length(e) < low do
if collapse e will make some edges longer than high
continue
collapse e at midpoint(e)
对于具体实现,关键在于边的遍历,特别注意 Edge Collapse 后相邻边的长度会发生改变,需要重新考虑。此外 Edge Collapse 会有不合法的情形。对于有边界的网格,一般要求边界不动,边两顶点不能是边界点。另外,如果边两顶点的邻点是边界点的话,collapse 后会导致新点度数增加(有大量的邻接点是边界点),因而也要求边两点的邻点没有边界点。
C++ 实现如下
unordered_set<E*> edges(mesh->Edges().begin(), mesh->Edges().end());
while(!edges.empty()){
auto iter = edges.begin();
E* e = *iter;
edges.erase(iter);
if(!e->IsCanCollapse(minL, maxL)) // test lots of contidons
continue;
vector<E*> eAdjEs = e->AdjEdges();
if(eAdjEs.size() <= 2)
continue; // dihedron
V* v = mesh->CollapseEdge(e, c);
if(v){ // collapse e success
for(E* eAdjE : eAdjEs)
edges,erase(eAdjE); // erase old edges
for(E* adjE : v->AdjEdges())
edges.insert(adjE); // add new edges
}
}
2.4 Equalize Valences
该步骤目的是平衡顶点的度数,最佳度数 optimal valence 为
- 内部点:6
- 边界点:4
如果 Edge Flip 能减少与最佳度数的差,则进行 Edge Flip,伪代码如下
equalize_valences()
for each edge e do
if flip e can reduce valence excess do
flip(e)
Edge Flip 会导致边原两端点度数减 1,新两端点度数加 1
这里有坑
- 对于三角网格,除了边界点度数可为 2,内部点度数都在 3 及以上,故若 Edge Flip 会产生 2 度内部点时不合法
- 边界边不可进行 Edge Flip 操作
- 对于凹四边形,flip 后会导致逆向的面
凹四边形
C++ 实现如下
vector<E*> edges = mesh->Edges();
for(E* e : edges){
if(e->IsBoundary())
continue;
int valenceExcess = /*...omit calulate ...*/;
int flipedValenceExcess = /*...omit calulate ...*/;
if(flipedValenceExcess >= valenceExcess)
continue;
if(/*non convex polygon*/)
continue;
flip(e);
}
2.5 Tangential Relaxation 和 Project to Surface
2.2 节和 2.3 节解决了边的长度问题,2.4 节解决了顶点度数问题,本节步骤解决的是三角形面积问题。
简单来说,将顶点向中心(邻接点的平均 ,其中
是顶点
的邻接点集,
是
的位置)平移即可,如下所示
我实现时采用了重力加权中心 gravity-weighted centroid[1],如下
其中 是
所在的两三角形的面积均值(对于边界点
,只有一个三角形)
另外平移时为了能保持原有网格的形状,且防止抖动,需要在切向进行平移,并且投影回原网格上,示例如下[4]
平移量为 ,切向平移为
(其中
是
的法向),从而得到未投影的点位置
顶点法向可用顶点周围三角形法向的带权平均,权值为面积,公式如下
其中 是
的邻接三角形集,
是三角形
的面积,
是
的法向
最后需要将点沿着法向投影回原网格上,点和法向构成的射线与 相交,涉及相交算法。
边界点不动。
3. 其他问题
3.1 二面体
算法可能在 Collapse Edge 后引入二面体(三个点,两个面,顺序相反)
此时应及时停止算法
3.2 并行
网格局部操作可以并行,但在进行操作时需要进行必要的锁资源。简单上锁整个网格就没法并行了,我采取的方案是锁相关顶点,比如 Edge Flip 需要锁住四个顶点。注意再加锁过程中可能有其他线程修改了邻接关系,导致锁点错误,因此需在最后检测所锁点是否仍为邻接点。因此并行的 overhead 比较大,效果不是很明显。
暂时只实现了 Edge Flip 的并行,日后会继续探究 Edge Spilt 和 Edge Collapse 的并行策略。
3.3 形状
2.5 节通过切向平移和投影一定程度上保持了网格形状,但还是会有轻微变化,在实际使用时,会人为指定特征点、边,算法中 Edge Flip、Edge Collapse 等要求不修改特征点、边,如此可进一步保持形状,较为繁杂,本文忽略。
4. 性能
CPU:Intel(R) Pentium(R) Gold G5400 CPU @ 3.70GHz
50, 000 顶点,100, 000 面的网格
进行 5 次循环,耗时 13s 左右
5. 测试
原网格
处理后网格
网格变均匀了,每个三角形大致都是等边三角形,且面积都挺小
另外,正如 3.3 节所述,部分区域的形状并没有很好保持住,主要就是一些特征边(龙尾,龙背等),大部分区域形状是保持的。
参考
- ^abBotsch M , Kobbelt L . A Remeshing Approach to Multiresolution Modeling[C]// Second Eurographics Symposium on Geometry Processing, Nice, France, July 8-10, 2004. 2004. http://www.graphics.rwth-aachen.de/media/papers/remeshing1.pdf
- ^Botsch M, Kobbelt L, Pauly M, et al. Polygon mesh processing[M]. AK Peters/CRC Press, 2010.
- ^UTD. 3D Geometric Modeling. https://personal.utdallas.edu/~praba/6v81/3dModeling.pdf
- ^CS468. Stanford Computer Graphics Laboratory. Remeshing I. http://graphics.stanford.edu/courses/cs468-12-spring/LectureSlides/13_Remeshing1.pdf