本文是借助openMesh库进行三维重建的孔洞填补的。openMesh里面有非常优秀的三维的数据结构。
核心:
找到三维模型当中所有的空洞,对于每一个孔洞,找出其所有的半边然后对其进行排序;对排序过的所有的半边,找到角度最小的两条边,增加第三条边,形成新的三角面片;如此迭代。具体的每一步的算法如下:
半边排序算法:
对于一个孔洞,找出其中的一条半边作为起始边,遍历所有其他的半边集合,找到以该条半边的尾点作为起点的半边;如此迭代,直到找到某一条边的尾点是第一条边的起点为止。计算角度:
对于上面这两种孔洞,箭头处可能角度都比较的小,但是图2 当中就是不能填补的。因此,计算的角度是和他们的方向是有关的。
计算的方法是:首先求出这个角度的大小(利用向量求出来一个0~180度之间的一个值angle,然后再确定由这两条边组成的一个面的法向量是否和第一条边对面的边所在的面的法向量一致,一致则输出角度,不一致则输出: 360-angle;计算距离:
两点的欧拉距离计算公式;
- 单个孔洞填补算法:
如下图所示:
如果 两点之间的距离过长(图3),则考虑在中间插入一个点(实际上是替换点),增加两个三角面片,用最外侧的两个半边句柄更新整个半边句柄;否则直接增加一个三角面片,更新句柄(图4)。
关键代码:
// 找到一个洞的半边集合, 排序
void DFS(int nNum, int n)// 在这n条半边当中找到并排序到 有序边对列的第 nNum 条处。 但这个只能是对一个孔洞
{
if (toTemp[nNum - 1] == fromTemp[0])// 首尾相接的意思吧,就是说找到最后一个了。 也就是完成了所有的边的排序
{
if (nNum < vNum)
{
vNum = nNum;//原来分配的空间大了, 这是这个环里面的边的条数, ******** 而n表示的是总的半边的数目
for (int i = 0; i < vNum;++i )
{
fromV[i] = fromTemp[i];// 直到找到了所有的半边后,才将所有的半边放入有序的半边集合,这样节省空间
toV[i] = toTemp[i];
}
}
return;
}
for (int i = 1; i < n;i ++)// n是给定的值,半边的个数
{
if (!vis[i] && toTemp[nNum - 1] == fromVetex[i])// 找到没有存储的且以 上一个尾点 为 起点的边
{
fromTemp[nNum] = fromVetex[i];
toTemp[nNum] = toVetex[i];