【问题标题】:Trouble Calculating Vertex Normals (in C++, for openGL project)计算顶点法线的麻烦(在 C++ 中,用于 openGL 项目)
【发布时间】:2013-12-27 10:36:36
【问题描述】:

我有一个项目,我必须读取 OBJ 文件并使用 GLUT 显示模型。我很好地阅读和显示模型,但我无法让法线正常工作。我已将所有面保存在 3 个 vtx[] (=points) 数组的 Faces[] 数组中,该数组具有 x、y、z 和一个法线,也具有 x、y、z 坐标。我计算了对应于每个面的 3 个顶点中的每一个的 2 个向量,我得到它们的叉积,我对其进行归一化并将其 x、y、z 坐标添加到 Normal 参数(这样如果有多个对于每个顶点的法线,我们将它们添加到类似于所有法线向量的平均值的东西),然后我再次进行归一化。当我打印出一些法线坐标以查看会发生什么时,它会打印出 0,0,(奇怪的东西)为 x,y,z。无论如何,这是代码。

     for (int i = 0 ; i<triangle_index; i++)
    {
        float ux,uy,uz,vx,vy,vz,nn,nx,ny,nz;

    //get vectors from first vertex of face
        ux= Faces[triangle_index].vtx[1].x - Faces[triangle_index].vtx[0].x ;
        uy= Faces[triangle_index].vtx[1].y - Faces[triangle_index].vtx[0].y ;
        uz= Faces[triangle_index].vtx[1].z - Faces[triangle_index].vtx[0].z ;

        vx= Faces[triangle_index].vtx[2].x - Faces[triangle_index].vtx[0].x ;
        vy= Faces[triangle_index].vtx[2].y - Faces[triangle_index].vtx[0].y ;
        vz= Faces[triangle_index].vtx[2].z - Faces[triangle_index].vtx[0].z ;
   //CrossProduct 
        nx = (uy*vz) - (vy*uz);
        ny = (vx*uz) - (ux*vz);
        nz = (ux*vy) - (uy*vx);
   //Length         
        nn = sqrt((nx*nx)+(ny*ny)+(nz*nz));
   //Normalize                  
        nx = nx/nn;
        ny = ny/nn;
        nz = nz/nn;
    //Save to Vertex.Normal (plus any previous data)
    Faces[triangle_index].vtx[0].norm.x =(Faces[triangle_index].vtx[0].norm.x + nx);
    Faces[triangle_index].vtx[0].norm.y =(Faces[triangle_index].vtx[0].norm.y + ny);
    Faces[triangle_index].vtx[0].norm.z =(Faces[triangle_index].vtx[0].norm.z + nz);

  //get length again 

  nn =  sqrt((Faces[triangle_index].vtx[0].norm.x*Faces[triangle_index].vtx[0].norm.x)+(Faces[triangle_index].vtx[0].norm.y*Faces[triangle_index].vtx[0].norm.y)+(Faces[triangle_index].vtx[0].norm.z*Faces[triangle_index].vtx[0].norm.z));

    //Normalize again

        Faces[triangle_index].vtx[0].norm.x =(Faces[triangle_index].vtx[0].norm.x)/nn;
        Faces[triangle_index].vtx[0].norm.y =(Faces[triangle_index].vtx[0].norm.y)/nn;
        Faces[triangle_index].vtx[0].norm.z =(Faces[triangle_index].vtx[0].norm.z)/nn;

   //---------------Same For Second Vertex of the Same Face---------------------------/

        ux= Faces[triangle_index].vtx[2].x - Faces[triangle_index].vtx[1].x ;
        uy= Faces[triangle_index].vtx[2].y - Faces[triangle_index].vtx[1].y ;
        uz= Faces[triangle_index].vtx[2].z - Faces[triangle_index].vtx[1].z ;

        vx= Faces[triangle_index].vtx[0].x - Faces[triangle_index].vtx[1].x ;
        vy= Faces[triangle_index].vtx[0].y - Faces[triangle_index].vtx[1].y ;
        vz= Faces[triangle_index].vtx[0].z - Faces[triangle_index].vtx[1].z ;

        nx = (uy*vz) - (vy*uz);
        ny = (vx*uz) - (ux*vz);
        nz = (ux*vy) - (uy*vx);

        nn = sqrt((nx*nx)+(ny*ny)+(nz*nz));

        nx = nx/nn;
        ny = ny/nn;
        nz = nz/nn;

        Faces[triangle_index].vtx[1].norm.x =(Faces[triangle_index].vtx[1].norm.x + nx);
        Faces[triangle_index].vtx[1].norm.y =(Faces[triangle_index].vtx[1].norm.y + ny);
        Faces[triangle_index].vtx[1].norm.z =(Faces[triangle_index].vtx[1].norm.z + nz);

    nn =sqrt((Faces[triangle_index].vtx[1].norm.x*Faces[triangle_index].vtx[1].norm.x)+(Faces[triangle_index].vtx[1].norm.y*Faces[triangle_index].vtx[1].norm.y)+(Faces[triangle_index].vtx[1].norm.z*Faces[triangle_index].vtx[1].norm.z));

        Faces[triangle_index].vtx[1].norm.x =(Faces[triangle_index].vtx[1].norm.x)/nn;
        Faces[triangle_index].vtx[1].norm.y =(Faces[triangle_index].vtx[1].norm.y)/nn;
        Faces[triangle_index].vtx[1].norm.z =(Faces[triangle_index].vtx[1].norm.z)/nn;
    //------------------Again for number three--------------------------------
    ux= Faces[triangle_index].vtx[0].x - Faces[triangle_index].vtx[2].x ;
    uy= Faces[triangle_index].vtx[0].y - Faces[triangle_index].vtx[2].y ;
    uz= Faces[triangle_index].vtx[0].z - Faces[triangle_index].vtx[2].z ;

    vx= Faces[triangle_index].vtx[1].x - Faces[triangle_index].vtx[2].x ;
    vy= Faces[triangle_index].vtx[1].y - Faces[triangle_index].vtx[2].y ;
    vz= Faces[triangle_index].vtx[1].z - Faces[triangle_index].vtx[2].z ;

    nx = (uy*vz) - (vy*uz);
    ny = (vx*uz) - (ux*vz);
    nz = (ux*vy) - (uy*vx);

    nn = sqrt((nx*nx)+(ny*ny)+(nz*nz));

    nx = nx/nn;
    ny = ny/nn;
    nz = nz/nn;

    Faces[triangle_index].vtx[2].norm.x =(Faces[triangle_index].vtx[2].norm.x + nx);
    Faces[triangle_index].vtx[2].norm.y =(Faces[triangle_index].vtx[2].norm.y + ny);
    Faces[triangle_index].vtx[2].norm.z =(Faces[triangle_index].vtx[2].norm.z + nz);

    nn =  sqrt((Faces[triangle_index].vtx[2].norm.x*Faces[triangle_index].vtx[2].norm.x)+(Faces[triangle_index].vtx[2].norm.y*Faces[triangle_index].vtx[2].norm.y)+(Faces[triangle_index].vtx[2].norm.z*Faces[triangle_index].vtx[2].norm.z));

    Faces[triangle_index].vtx[2].norm.x =(Faces[triangle_index].vtx[2].norm.x)/nn;
    Faces[triangle_index].vtx[2].norm.y =(Faces[triangle_index].vtx[2].norm.y)/nn;
    Faces[triangle_index].vtx[2].norm.z =(Faces[triangle_index].vtx[2].norm.z)/nn;

   } 

如果我添加这个循环只是为了看看在计算法线后会发生什么

for (int i =0 ; i<100;i++)
cout << "n "<<  Faces[i].vtx[0].norm.x<< " "<< Faces[i].vtx[0].norm.x <<" "<<  Faces[i].vtx[0].norm.x <<"\n";

我得到了 norm.x,y,z 的全零。 任何人都可以看到代码有什么问题吗?

【问题讨论】:

    标签: c++ opengl vector


    【解决方案1】:

    您在到处使用triangle_index 为您的数组索引:

        ux= Faces[triangle_index].vtx[1].x - Faces[triangle_index].vtx[0].x ;
        uy= Faces[triangle_index].vtx[1].y - Faces[triangle_index].vtx[0].y ;
        uz= Faces[triangle_index].vtx[1].z - Faces[triangle_index].vtx[0].z ;
    

    我认为您想改用循环索引:

        ux= Faces[i].vtx[1].x - Faces[i].vtx[0].x ;
        uy= Faces[i].vtx[1].y - Faces[i].vtx[0].y ;
        uz= Faces[i].vtx[1].z - Faces[i].vtx[0].z ;
    

    您需要在整个循环中解决此问题。

    此外,要正确平均顶点处的法线,您应该只在此循环中累积它们,并在新的第二个循环中重新归一化它们。也就是把这两个步骤分两遍:

        Faces[i].vtx[0].norm.x += nx;
        Faces[i].vtx[0].norm.y += ny;
        Faces[i].vtx[0].norm.z += nz;
    

        nn = sqrt( (Faces[i].vtx[0].norm.x*Faces[i].vtx[0].norm.x)
                 + (Faces[i].vtx[0].norm.y*Faces[i].vtx[0].norm.y)
                 + (Faces[i].vtx[0].norm.z*Faces[i].vtx[0].norm.z) );
    
        Faces[i].vtx[0].norm.x /= nn;
        Faces[i].vtx[0].norm.y /= nn;
        Faces[i].vtx[0].norm.z /= nn;
    

    这样,与该顶点重合的每个三角形都具有相同的权重。

    【讨论】:

    • 好收获。是的,那会得到很多零。还有一些除零。
    • 嗨!谢谢你的回复。你是对的,但这是我在这里犯的一个错误。因为我只是试图改变一些东西,我还没有清理它。我确实在原始代码中更改了它,但现在正在计算法线,它们不是零,但它们看起来是错误的。
    • @user3085239 :顺便说一句,如果您将顶点法线计算为该顶点处所有叉积的平均值,您应该this 循环,然后在第二遍中重新规范化。如果您对添加到累积中的每个新法线进行重新归一化,那么后续法线将承担过多的权重。
    • 我不确定自己是否诚实。我认为它会起作用,因为我添加然后我正常化。我想不出一种方法来获得每个向量的平均值,而不是简单地替换它们。
    • @user3085239 :我只是建议将其分成两个循环。在上面的循环中,只需执行“norm.x = norm.x + nx”等。在第二个循环中,重新归一化所有累积的法线。
    【解决方案2】:

    您的代码可能完全有效,而问题在于您的法线方向。如果您在同一平面上有两个三角形在一个点相交,那么在顶点计算的法线可能是相反的方向(例如,如果这些点在 XY 平面上,您可能有一个法线 [0 0 1] 和其他 [0 0 -1]),取决于顶点的顺序。

    当你把这两个向量相加时,你可能会得到一个长度为零的向量,这会给你归一化带来麻烦。

    我会检查向量的长度,也许会选择一个“主导方向”来防止这个问题。或者总是分配三角形的值“其质心最接近顶点”,或“质心具有最低 X,Y 值” - 等等。

    【讨论】:

    • 所以你建议我检查一下顶点是否已经分配了一个更大长度的法线,如果有,请保持原样?
    • 与该顶点“几乎平行”的顶点表示可能出现问题。平行:abs(dot(n1, n2)) > 0.999(比如说) - 其中 n1 和 n2 被归一化。
    • 好吧,我试试这个!但是现在是早上 6 点,我现在不能再看代码了!非常感谢。明天回来报告!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 1970-01-01
    • 2011-08-04
    • 2013-02-15
    • 2011-12-26
    • 1970-01-01
    • 2019-03-30
    相关资源
    最近更新 更多