c++ - 计算三角形网格中的法线

标签 c++ opengl computational-geometry normals

我画了一个有 10000 个顶点(100x100)的三角形网格,它将是一个草地。我为此使用了 gldrawelements()。我已经看了一整天,仍然无法理解如何为此计算法线。每个顶点都有自己的法线还是每个三角形都有自己的法线?有人可以指出我如何编辑我的代码以合并法线的正确方向吗?

struct vertices {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}vertices[10000];

GLuint indices[60000];

/*
99..9999
98..9998
........
01..9901
00..9900
*/

void CreateEnvironment() {
    int count=0;
    for (float x=0;x<10.0;x+=.1) {
        for (float z=0;z<10.0;z+=.1) {
            vertices[count].x=x;
            vertices[count].y=0;
            vertices[count].z=z;
            count++;
        }
    }
    count=0;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            GLuint v1=(a*100)+b;indices[count]=v1;count++;
            GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
            GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
        }
    }
    count=30000;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            indices[count]=(a*100)+b+100;count++;//9998
            indices[count]=(a*100)+b+1;count++;//9899
            indices[count]=(a*100)+b+101;count++;//9999
        }
    }
}

void ShowEnvironment(){
    //ground
    glPushMatrix();
    GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
    glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
    glEnableClientState(GL_VERTEX_ARRAY);
    glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );
    glVertexPointer(3,GL_FLOAT,0,vertices);
    glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
    glDisableClientState(GL_VERTEX_ARRAY);
    glPopMatrix();
}

编辑 1
这是我写出来的代码。我只是使用数组而不是 vector ,并将所有法线存储在称为法线的结构中。然而它仍然不起作用。我在 *indices 处得到一个未处理的异常。
struct Normals {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}normals[20000];
Normals* normal = normals;
//***************************************ENVIRONMENT*************************************************************************
struct vertices {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}vertices[10000];

GLuint indices[59403];

/*
99..9999
98..9998
........
01..9901
00..9900
*/

void CreateEnvironment() {
    int count=0;
    for (float x=0;x<10.0;x+=.1) {
        for (float z=0;z<10.0;z+=.1) {
            vertices[count].x=x;
            vertices[count].y=rand()%2-2;;
            vertices[count].z=z;
            count++;
        }
    }
    //calculate normals 
    GLfloat vector1[3];//XYZ
    GLfloat vector2[3];//XYZ
    count=0;
    for (int x=0;x<9900;x+=100){
        for (int z=0;z<99;z++){
            vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x
            vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
            vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
            vector2[0]= vertices[x+z+1].x-vertices[x+z+100].x;//vector2x
            vector2[1]= vertices[x+z+1].y-vertices[x+z+100].y;//vector2y
            vector2[2]= vertices[x+z+1].z-vertices[x+z+100].z;//vector2z
            normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
            normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
            normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
        }
    }
    count=10000;
    for (int x=100;x<10000;x+=100){
        for (int z=0;z<99;z++){
            vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x -- JUST ARRAYS
            vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
            vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
            vector2[0]= vertices[x+z+1].x-vertices[x+z-100].x;//vector2x
            vector2[1]= vertices[x+z+1].y-vertices[x+z-100].y;//vector2y
            vector2[2]= vertices[x+z+1].z-vertices[x+z-100].z;//vector2z
            normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
            normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
            normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
        }
    }

    count=0;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            GLuint v1=(a*100)+b;indices[count]=v1;count++;
            GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
            GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
        }
    }
    count=30000;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            indices[count]=(a*100)+b+100;count++;//9998
            indices[count]=(a*100)+b+1;count++;//9899
            indices[count]=(a*100)+b+101;count++;//9999
        }
    }
}

void ShowEnvironment(){
    //ground
    glPushMatrix();
    GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
    GLfloat GroundDiffuse[]={1.0,0.0,0.0,1.0};
    glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
    glMaterialfv(GL_FRONT,GL_DIFFUSE,GroundDiffuse);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer( GL_FLOAT, 0, normal);
    glVertexPointer(3,GL_FLOAT,0,vertices);
    glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glPopMatrix();
}
//***************************************************************************************************************************

最佳答案

Does each vertex have its own normals or does each triangle have its own normals?



就像很多时候一样,答案是:“这取决于”。由于法线被定义为垂直于给定平面(N 维)内所有 vector 的 vector ,因此您需要一个平面来计算法线。顶点位置只是一个点,因此是奇异的,因此您实际上需要一个面来计算法线。因此,天真地,人们可以假设法线是每个面的,因为法线计算的第一步是通过评估面边缘的叉积来确定面法线。

假设您有一个带点的三角形 一个 , , C ,那么这些点有位置 vector ↑A , ↑B , ↑C 并且边有 vector ↑B - ↑A ↑C - ↑A 所以面法 vector 是 ↑Nf = (↑B - ↑A) × (↑C - ↑A)

注意的幅度↑NF 如上所述,它与面部面积成正比。

在光滑表面中,顶点在面之间共享(或者您可以说这些面共享一个顶点)。在这种情况下,顶点的法线不是它所属面的面法线之一,而是它们的线性组合:

↑Nv = ∑ p ↑Nf ;哪里电话 是每个面的权重。

人们可以假设参与的面部法线之间的权重相等。但假设一张脸越大,它对法线的贡献就越大,这更有意义。

现在回想一下,您通过 vector 进行归一化。 ↑v 通过用它的倒数长度缩放它: ↑vi = ↑v/|↑v| .但如前所述,面部法线的长度已经取决于面部的面积。所以权重因子电话 上面给出的已经包含在 vector 本身中:它的长度,也就是大小。所以我们可以通过简单地将所有面法线相加得到顶点法线 vector 。

在光照计算中,法 vector 必须是单位长度,即归一化为可用。所以总结后,我们将新发现的顶点法线归一化并使用它。

细心的读者可能已经注意到我特别说过光滑的表面共享顶点。事实上,如果您的几何体中有一些折痕/硬边,则两侧的面不会共享顶点。在 OpenGL 中,顶点是
  • 职位
  • 正常
  • (彩色)
  • N个纹理坐标
  • M 进一步属性

  • 你改变其中一个,你会得到一个完全不同的顶点。现在,一些 3D 建模者只将顶点视为一个点的位置,并存储每个面的其余属性(Blender 就是这样的建模者)。这节省了一些内存(或相当大的内存,取决于属性的数量)。但是 OpenGL 需要全部,所以如果使用这样一个混合范式文件,您必须首先将其分解为 OpenGL 兼容数据。看看 Blender 的导出脚本之一,比如 PLY 导出器,看看它是如何完成的。

    现在来介绍一些其他的东西。在你的代码中,你有这个:
     glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );
    

    索引指针有 没什么与顶点数组索引有关!这是过去时代的时代错误,当时图形仍然使用调色板而不是真彩色。像素颜色不是通过给出它的 RGB 值来设置的,而是通过偏移到有限的调色板中的单个数字来设置的。调色板颜色仍然可以在几种图形文件格式中找到,但没有像样的硬件再使用它们。

    请从您的内存和代码中删除 glIndexPointer(和 glIndex),它们不会按照您的想法执行整个索引颜色模式使用起来很神秘,坦率地说,我不知道 1998 年之后构建的任何硬件仍然支持它。

    关于c++ - 计算三角形网格中的法线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6656358/

    相关文章:

    python - 仅使用一张深度图像的 OpenCV 视差图后过滤

    computational-geometry - CGAL:线段和多边形之间的交点?

    java - 如何使用 .jar 中打包的 .png 数据作为 LWJGL 中的纹理?

    c++ - 如何删除网格查看器上选择性表面内的边缘?

    algorithm - 四面体网格中的点位置

    c++ - 为什么我的指针输出一个字符串而不是 C++ 中的内存地址?

    c++ - Visual Studio Code : C++ include path

    c++ - 使用 ofstream 写入没有 ios::binary 标志 C++ 的二进制文件

    c++ - 在 openGL 中,我如何从内存中编辑特定的顶点缓冲区属性

    c++ - glLoadMatrixf 似乎不起作用