【问题标题】:How to unify normal orientation如何统一法线方向
【发布时间】:2013-09-01 20:01:23
【问题描述】:

我一直在尝试实现一个所有面法线都指向外部的网格。 为了实现这一点,我从 *.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 仍然显示法线面向两侧)

【问题讨论】:

    标签: c++ 3d normals


    【解决方案1】:

    我终于解决了这个问题。所以我仍在使用 VCG 库。从上面的新代码中,我稍微更新了以下部分:

    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);
    

    现在我更新了clean.h 中的vcg::tri::Clean&lt;MyMesh&gt;::OrientCoherentlyMesh() 函数。这里的更新是正确定位组的第一个多边形。同样在交换边缘后,面部的法线也会被计算和更新。

    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;
        }
    }
    

    此外,我还更新了函数bool CheckOrientation(FaceType &amp;f, int z) 以执行基于正常z 方向的计算。

    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;
        }
    }
    

    结果正如我对算法的期望和期望:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-29
      • 1970-01-01
      • 1970-01-01
      • 2011-08-04
      • 2021-01-30
      • 2019-12-17
      • 2012-02-20
      相关资源
      最近更新 更多