【OpenMesh】OpenMesh实现增量网格重构
本文实现的为增量网格重构(incremental remesh)算法思想如下:
- 接收一个目标边长作为输入;
- 遍历所有边,边长度大于目标边长的4/3时,认为是长边,将在该边中心点新增点,分割该边,做相应的连接操作;
- 遍历所有边,若边长度小于目标边长的4/5时,认为是短边,将于邻接边合并;
- 翻转边,使三角形的内角尽可能的趋近于60,避免钝角;
- 对模型进行切向平滑操作。
对上述步骤循环5次左右,即可得到较为均匀的网格。
算法具体实现时使用了OpenMesh库,使用了库中自带的函数,包含split()、collapse()、flip()和smooth()函数。
main()
mesh.request_vertex_status();mesh.request_edge_status();mesh.request_halfedge_status();mesh.request_face_status();EdgeFlipFunc(mesh);doubletargetLength=50;for(inti=0;i<5;i++){EdgeSplitFunc(mesh,targetLength);EdgeColapaseFunc(mesh,targetLength);EdgeFlipFunc(mesh);MeshSmoothFunc(mesh);mesh.garbage_collection();}- 需要request_XXXX_status()
- 首先进行一次
flip操作,能提高某些自动生成的网格的质量(如openmesh中构建一个平行四边形,左下角为起点,向右倾斜,这时使用TriMesh_ArrayKernelT的mesh会自动连接点0和2,显然边0-2的三角化方式生成的三角网格质量没有连接1-3的方式好
) garbage_collection()因未仔细研究,所以尽量都添加上了
EdgeSplitFunc()
voidEdgeSplitFunc(MyMesh&mesh,doubletargetLength){doublemax=targetLength*4/3;MyMesh::EdgeIterEnd(mesh.edges_end());OpenMesh::Vec3d midPt;for(MyMesh::EdgeIter iter=mesh.edges_sbegin();iter!=End;++iter){if(max<(mesh.point((*iter).v0())-mesh.point((*iter).v1())).length()){midPt=(mesh.point((*iter).v0())+mesh.point((*iter).v1()))/2;mesh.split(*iter,(mesh.point((*iter).v0())+mesh.point((*iter).v1()))/2);)/2<<endl;}}mesh.garbage_collection();}- 4/3为经验值,
- 使用
MyMesh::EdgeIter End(mesh.edges_end()),而不是iter!=mesh.edges_end(),是为了避免递归的分割长边。一次循环中,只分割当前存在的边,不分割新生成的边。
EdgeColapaseFunc()
voidEdgeColapaseFunc(MyMesh&mesh,doubletargetLength){doublemin=targetLength*4/5;MyMesh::EdgeIterEnd(mesh.edges_end());for(autoiter=mesh.edges_sbegin();iter!=End;++iter){if((*iter).is_boundary()||(*iter).v0().is_boundary()||(*iter).v1().is_boundary())continue;if(min>(mesh.point((*iter).v0())-mesh.point((*iter).v1())).length()){if(mesh.is_collapse_ok((*iter).h0()))mesh.collapse((*iter).h0());if(mesh.is_collapse_ok((*iter).h1()))mesh.collapse((*iter).h1());}}mesh.garbage_collection();}- 同split,4/5是经验值
- 同样为了避免递归合并短边,使用了
MyMesh::EdgeIter End(mesh.edges_end()); - 合并短边时,要求合并的边不是网格边界边,严格的边的两个点也不可以是边界点
- 合并短边使用的半边结构,带有方向,为避免改变网格拓扑
EdgeFlipFunc()
voidEdgeFlipFunc(MyMesh&mesh){doubleorigin,after;MyMesh::EdgeIter iter;for(iter=mesh.edges_sbegin();iter!=mesh.edges_end();++iter){if(mesh.is_flip_ok(*iter)){origin=(mesh.point((*iter).v0())-mesh.point((*iter).v1())).length();mesh.flip(*iter);after=(mesh.point((*iter).v0())-mesh.point((*iter).v1())).length();if(origin<after)mesh.flip(*iter);}}mesh.garbage_collection();}- flip只是简单的判断两种方式边的长度大小,使用短的那一种方式
MeshSmoothFunc()
voidMeshSmoothFunc(MyMesh&mesh){OpenMesh::Smoother::SmootherT<MyMesh>::Component com=OpenMesh::Smoother::SmootherT<MyMesh>::Tangential;OpenMesh::Smoother::SmootherT<MyMesh>::Continuity con=OpenMesh::Smoother::SmootherT<MyMesh>::C0;OpenMesh::Smoother::JacobiLaplaceSmootherT<MyMesh>smoother(mesh);smoother.initialize(com,con);smoother.smooth(5);}- 需要使用切向平滑,带有法向的平滑会使网格变成处在一个平面
参考文献
- Botsch 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