7

我一直在尝试实现一个所有面法线都指向外部的网格。为了实现这一点,我从 *.ctm 文件中加载了一个网格,然后遍历所有三角形以使用叉积确定法线,如果法线指向负 z 方向,我翻转 v1 和 v2(因此正常方向)。完成后,我将结果保存到 *.ctm 文件并使用 Meshlab 进行查看。

Meshlab 中的结果仍然显示法线指向正和负 z 方向(可以从黑色三角形中看出)。同样,在 Meshlab 中查看法线时,它们确实是向后指向的。

谁能给我一些关于如何解决这个问题的建议?

规范化部分的源代码是:

pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ());
pcl::fromROSMsg (meshFixed.cloud,*cloud1);for(std::vector<pcl::Vertices>::iterator it = meshFixed.polygons.begin(); it != meshFixed.polygons.end(); ++it)
{
    alglib::real_2d_array v0;
    double _v0[] = {cloud1->points[it->vertices[0]].x,cloud1->points[it->vertices[0]].y,cloud1->points[it->vertices[0]].z};
    v0.setcontent(3,1,_v0); //3 rows, 1col
    alglib::real_2d_array v1;
    double _v1[] = {cloud1->points[it->vertices[1]].x,cloud1->points[it->vertices[1]].y,cloud1->points[it->vertices[1]].z};
    v1.setcontent(3,1,_v1); //3 rows, 1col
    alglib::real_2d_array v2;
    double _v2[] = {cloud1->points[it->vertices[2]].x,cloud1->points[it->vertices[2]].y,cloud1->points[it->vertices[2]].z};
    v2.setcontent(1,3,_v2); //3 rows, 1col
    alglib::real_2d_array normal;
    normal = cross(v1-v0,v2-v0);
    //if z<0 change indices order v1->v2 and v2->v1
    alglib::real_2d_array normalizedNormal;
    if(normal[2][0]<0)
    {
            int index1,index2;
            index1 = it->vertices[1];
            index2 = it->vertices[2];
            it->vertices[1] = index2;
            it->vertices[2] = index1;
            //make normal of length 1
            double normalScaling = 1.0/sqrt(dot(normal,normal));
            normal[0][0] = -1*normal[0][0];
            normal[1][0] = -1*normal[1][0];
            normal[2][0] = -1*normal[2][0];
            normalizedNormal = normalScaling * normal;
    }
    else
    {
            //make normal of length 1
            double normalScaling = 1.0/sqrt(dot(normal,normal));
            normalizedNormal = normalScaling * normal;
    }
    //add to normal cloud
    pcl::Normal pclNormalizedNormal;
    pclNormalizedNormal.normal_x = normalizedNormal[0][0];
    pclNormalizedNormal.normal_y = normalizedNormal[1][0];
    pclNormalizedNormal.normal_z = normalizedNormal[2][0];
    normalsFixed.push_back(pclNormalizedNormal);
} 

这段代码的结果是:
在此处输入图像描述

我在 VCG 库中找到了一些代码来定位面和顶点法线。使用此功能后,大部分网格具有正确的面法线,但不是全部。

新代码:

// VCG library implementation
    MyMesh m;
    // Convert pcl::PolygonMesh to VCG MyMesh
    m.Clear();
    // Create temporary cloud in to have handy struct object
    pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud1 (new pcl::PointCloud<pcl::PointXYZRGBA> ());
    pcl::fromROSMsg (meshFixed.cloud,*cloud1);
    // Now convert the vertices to VCG MyMesh
    int vertCount = cloud1->width*cloud1->height;
    vcg::tri::Allocator<MyMesh>::AddVertices(m, vertCount);
    for(unsigned int i=0;i<vertCount;++i)
        m.vert[i].P()=vcg::Point3f(cloud1->points[i].x,cloud1->points[i].y,cloud1->points[i].z);
    // Now convert the polygon indices to VCG MyMesh => make VCG faces..
    int triCount = meshFixed.polygons.size();
    if(triCount==1)
    {
        if(meshFixed.polygons[0].vertices[0]==0 && meshFixed.polygons[0].vertices[1]==0 && meshFixed.polygons[0].vertices[2]==0)
            triCount=0;
    }
    Allocator<MyMesh>::AddFaces(m, triCount);
    for(unsigned int i=0;i<triCount;++i)
    {
        m.face[i].V(0)=&m.vert[meshFixed.polygons[i].vertices[0]];
        m.face[i].V(1)=&m.vert[meshFixed.polygons[i].vertices[1]];
        m.face[i].V(2)=&m.vert[meshFixed.polygons[i].vertices[2]];
    }

    vcg::tri::UpdateBounding<MyMesh>::Box(m);
    vcg::tri::UpdateNormal<MyMesh>::PerFace(m);
    vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
    printf("Input mesh  vn:%i fn:%i\n",m.VN(),m.FN());

    // Start to flip all normals to outside
    vcg::face::FFAdj<MyMesh>::FFAdj();
    vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
    bool oriented, orientable;
    if ( vcg::tri::Clean<MyMesh>::CountNonManifoldEdgeFF(m)>0 ) {
        std::cout << "Mesh has some not 2-manifold faces, Orientability requires manifoldness" << std::endl; // text
        return; // can't continue, mesh can't be processed
    }
    vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable);
    vcg::tri::Clean<MyMesh>::FlipNormalOutside(m);
    vcg::tri::Clean<MyMesh>::FlipMesh(m);
    //vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
    //vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m);
    vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
    vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m);

    // now convert VCG back to pcl::PolygonMesh
    pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGBA>);
    cloud->is_dense = false;
    cloud->width = vertCount;
    cloud->height = 1;
    cloud->points.resize (vertCount);
    // Now fill the pointcloud of the mesh
    for(int i=0; i<vertCount; i++)
    {
        cloud->points[i].x = m.vert[i].P()[0];
        cloud->points[i].y = m.vert[i].P()[1];
        cloud->points[i].z = m.vert[i].P()[2];
    }
    pcl::toROSMsg(*cloud,meshFixed.cloud);
    std::vector<pcl::Vertices> polygons;
    // Now fill the indices of the triangles/faces of the mesh
    for(int i=0; i<triCount; i++)
    {
        pcl::Vertices vertices;
        vertices.vertices.push_back(m.face[i].V(0)-&*m.vert.begin());
        vertices.vertices.push_back(m.face[i].V(1)-&*m.vert.begin());
        vertices.vertices.push_back(m.face[i].V(2)-&*m.vert.begin());
        polygons.push_back(vertices);
    }
    meshFixed.polygons = polygons;

这导致:(Meshlab 仍然显示法线面向两侧)
在此处输入图像描述

4

1 回答 1

8

I finally solved the problem. So I'm still using VCG library. From the above new code I slightly updated the following section:

vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh(m, oriented,orientable);
//vcg::tri::Clean<MyMesh>::FlipNormalOutside(m);
//vcg::tri::Clean<MyMesh>::FlipMesh(m);
//vcg::tri::UpdateTopology<MyMesh>::FaceFace(m);
//vcg::tri::UpdateTopology<MyMesh>::TestFaceFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexNormalizedPerFace(m);
vcg::tri::UpdateNormal<MyMesh>::PerVertexFromCurrentFaceNormal(m);

Now I've updated the vcg::tri::Clean<MyMesh>::OrientCoherentlyMesh() function in clean.h. Here the update is to orient the first polygon of a group correctly. Also after swapping the edge the normal of the face is calculated and updated.

static void OrientCoherentlyMesh(MeshType &m, bool &Oriented, bool &Orientable)
{
    RequireFFAdjacency(m);
    assert(&Oriented != &Orientable);
    assert(m.face.back().FFp(0));    // This algorithms require FF topology initialized

    Orientable = true;
    Oriented = true;

    tri::UpdateSelection<MeshType>::FaceClear(m);
    std::stack<FacePointer> faces;

    for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
    {
        if (!fi->IsD() && !fi->IsS())
        {
            // each face put in the stack is selected (and oriented)
            fi->SetS();
            // New section of code to orient the initial face correctly
            if(fi->N()[2]>0.0)
            {
                face::SwapEdge<FaceType,true>(*fi, 0);
                face::ComputeNormal(*fi);
            }
            // End of new code section.
            faces.push(&(*fi));

            // empty the stack
            while (!faces.empty())
            {
                FacePointer fp = faces.top();
                faces.pop();

                // make consistently oriented the adjacent faces
                for (int j = 0; j < 3; j++)
                {
                   //get one of the adjacent face
                   FacePointer fpaux = fp->FFp(j);
                   int iaux = fp->FFi(j);

                   if (!fpaux->IsD() && fpaux != fp && face::IsManifold<FaceType>(*fp, j))
                   {              
                      if (!CheckOrientation(*fpaux, iaux))
                      {
                          Oriented = false;

                          if (!fpaux->IsS())
                          {
                               face::SwapEdge<FaceType,true>(*fpaux, iaux);
                               // New line to update face normal
                               face::ComputeNormal(*fpaux);
                               // end of new section.
                               assert(CheckOrientation(*fpaux, iaux));
                          }
                          else
                          {
                               Orientable = false;
                               break;
                          }
                       }

                       // put the oriented face into the stack

                       if (!fpaux->IsS())
                       {
                            fpaux->SetS();
                            faces.push(fpaux);
                       }
                   }
               }
           }
       }
       if (!Orientable) break;
    }
}

Besides I also updated the function bool CheckOrientation(FaceType &f, int z) to perform a calculation based on normal z-direction.

template <class FaceType>
bool CheckOrientation(FaceType &f, int z)
{
    // Added next section to calculate the difference between normal z-directions
    FaceType *original = f.FFp(z);
    double nf2,ng2;
    nf2=f.N()[2];
    ng2=original->N()[2];
    // End of additional section
    if (IsBorder(f, z))
        return true;
    else
    {
        FaceType *g = f.FFp(z);
        int gi = f.FFi(z);
        // changed if statement from: if (f.V0(z) == g->V1(gi))
        if (nf2/abs(nf2)==ng2/abs(ng2))
            return true;
        else
            return false;
    }
}

The result is as I expect and desire from the algorithm:

enter image description here

于 2013-09-04T10:10:32.983 回答