如何统一法线方向 [英] How to unify normal orientation

查看:32
本文介绍了如何统一法线方向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been trying to realize a mesh that has all face normals pointing outward. In order to realize this, I load a mesh from a *.ctm file, then walk over all triangles to determine the normal using a cross product and if the normal is pointing to the negative z direction, I flip v1 and v2 (thus the normal orientation). After this is done I save the result to a *.ctm file and view it with Meshlab.

Meshlab 中的结果仍然显示法线同时指向正方向和负 z 方向(可以从黑色三角形看到).还有观看的时候Meshlab 中的法线实际上是向后指向的.

The result in Meshlab still shows that normals are pointing in both positive and negative z direction ( can be seen from the black triangles). Also when viewing the normals in Meshlab they are really pointing backwards.

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

Can anyone give me some advice on how to solve this?

归一化部分的源代码为:

The source code for the normalization part is:

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

这段代码的结果是:

The result from this code is:

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

I've found some code in the VCG library to orient the face and vertex normals. After using this a large part of the mesh has correct face normals, but not all.

新代码:

// 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
",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 仍然显示法线面向两侧)

Which results in: (Meshlab still shows normals are facing both sides)

推荐答案

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

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

现在我已经更新了 clean.h 中的 vcg::tri::Clean::OrientCoherentlyMesh() 函数.这里的更新是正确定位组的第一个多边形.同样在交换边缘后,计算并更新面部的法线.

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

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

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:

这篇关于如何统一法线方向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆