计算三角形网格中的法线 [英] Calculating normals in a triangle mesh

查看:159
本文介绍了计算三角形网格中的法线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

GLuint索引[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
这是我写的代码。我只是使用数组而不是矢量,我存储所有的法线在结构中称为法线。它仍然不工作。我在* index获得未处理的异常。

  struct Normals {
GLfloat x;
GLfloat y;
GLfloat z;
} normals [20000];
法线*法线=法线;
// *************************************** ENVIRONMENT **** **************************************************** *******************
struct vertices {
GLfloat x;
GLfloat y;
GLfloat z;
} vertices [10000];

GLuint索引[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 ++;
}
}
//计算法线
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];
normalals [count] .y = vector1 [2] * vector2 [0] - vector1 [0] * vector2 [2]
normalals [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
normalals [count] .x = vector1 [1] * vector2 [2] -vector1 [2] * vector2 [1];
normalals [count] .y = vector1 [2] * vector2 [0] - vector1 [0] * vector2 [2]
normalals [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();
}
// *************************************** *************************************************** **********************************
每个顶点都有自己的法线吗?

像这样经常的答案是:这取决于。由于法线被定义为垂直于给定平面内所有矢量的向量(在N维中),因此您需要一个平面来计算法线。顶点位置只是一个点,因此是奇异的,所以你实际上需要一个面来计算法线。因此,天真地,可以假设每个面部的法线是正的,因为正常计算中的第一步是通过评估面部边缘的叉积来确定面部法线。



假设您有一个包含点 A B C 的三角形,则这些点具有位置向量 ↑A ↑C ,边缘具有向量↑B - ↑A ↑C < ↑A ,所以面法线向量为↑N f =(↑B - ↑A)×(↑C - ↑A) b
$ b

请注意,如上所述,↑N f 的大小与面部的面积成正比。



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



↑N v =Σp↑N f ;其中 p 是每个面部的加权。



可以假设参与面部法线之间具有相等的加权。但是更有意义的是,假设一个面部越大,它对正常的贡献越大。



现在回想一下,通过向量进行归一化↑v 通过缩放它的recipocal长度:↑v i =↑v / |↑v | 。但正如已经告诉的,面部法线的长度已经取决于面部的面积。因此,上面给出的加权因子 p 已经包含在向量本身中:它的长度,也称为幅度。因此,我们可以通过简单地对所有面法线求和来获得顶点法线向量。在照明计算中,法线向量必须是单位长度,即标准化为可用的。因此,在总结之后,我们对新发现的顶点法线进行归一化并使用。



小心的读者可能已经注意到我明确地说,顶点。事实上,如果你的几何中有一些折痕/硬边缘,那么两边的面不共享顶点。在OpenGL中,顶点是




    的整个组合
  • 位置

  • 正常

  • (颜色)

  • N纹理坐标

  • M其他属性



你改变其中一个,你有一个完全不同的顶点。现在一些3D建模者只将一个顶点视为点的位置,并存储每个面的其余属性(Blender就是这样一个建模器)。这节省了一些内存(或相当多的内存,取决于属性的数量)。但OpenGL需要整个事情,所以如果使用这样一个混合范式文件,你必须将其分解为OpenGL兼容的数据。看看Blender的导出脚本之一,像PLY出口商看看它是怎么做的。






其他东西。在您的代码中,您具有:

  glIndexPointer(GL_UNSIGNED_BYTE,0,indices); 

索引指针与顶点数组索引这是一个anachronsim从天,当图形仍然使用调色板,而不是真正的颜色。像素颜色不是通过给定RGB值来设置的,而是通过一个数字抵消成有限调色板的颜色。



请清除 glIndexPointer (和glIndex )从你的内存和你的代码,他们不做你认为他们做的整个索引的颜色模式是奥术使用,坦率地说,我不知道任何硬件在1998年后仍然支持它。


I have drawn a triangle mesh with 10000 vertices(100x100) and it will be a grass ground. I used gldrawelements() for it. I have looked all day and still can't understand how to calculate the normals for this. Does each vertex have its own normals or does each triangle have its own normals? Can someone point me in the right direction on how to edit my code to incorporate normals?

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

EDIT 1 Here is the code I have written out. I just used arrays instead of vectors and I stored all of the normals in the struct called normals. It still doesn't work however. I get an unhandled exception at *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?

Like so often the answer is: "It depends". Since a normal is defined as being the vector perpendicular to all vectors within a given plane (in N dimensions), you need a plane to calculate a normal. A vertex position is just a point and thus singular, so you actually need a face to calculate the normal. Thus, naively, one could assume that normals are per face as the first step in normal calculation is determining the face normals, by evaluating the cross product of the faces edges.

Say you have a triangle with points A, B, C, then these points have position vectors ↑A, ↑B, ↑C and the edges have vectors ↑B - ↑A and ↑C - ↑A so the face normal vector is ↑Nf = (↑B - ↑A) × (↑C - ↑A)

Note that the magnitude of ↑Nf as it's stated above is directly proportional to the face's area.

In smooth surfaces vertices are shared between faces (or you could say those faces share a vertex). In that case the normal at the vertex is not one of the face normals of the faces it is part of, but a linear combination of them:

↑Nv = ∑ p ↑Nf ; where p is a weighting for each face.

One could either assume a equal weighting between the participating face normals. But it makes more sense to assume that the larger a face is, the more it contributes to the normal.

Now recall that you normalize by a vector ↑v by scaling it with it's recipocal length: ↑vi = ↑v/|↑v|. But as already told the length of the face normals already depends on the face's area. So the weighting factor p given above is already contained in the vector itself: Its length, aka magnitude. So we can get the vertex normal vector by simply summing up all the face normals.

In lighting calculations the normal vector must be unit length, i.e. normalized to be useable. So after summing up, we normalize the newly found vertex normal and use that.

The carefull reader may have noticed I specifically said smooth surfaces share vertices. And in fact, if you have some creases / hard edges in your geometry, then the faces on either side don't share vertices. In OpenGL a vertex is the whole combination of

  • position
  • normal
  • (colour)
  • N texture coordinates
  • M further attributes

You change one of these and you got a completely different vertex. Now some 3D modelers see a vertex only as a point's position and store the rest of those attributes per face (Blender is such a modeler). This saves some memory (or considerable memory, depending on the number of attributes). But OpenGL needs the whole thing, so if working with such a mixed paradigm file you will have to decompose it into OpenGL compatible data first. Have a look at one of Blender's export scripts, like the PLY exporter to see how it's done.


Now to cover some other thing. In your code you have this:

 glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );

The index pointer has nothing to do with vertex array indices! This is an anachronsim from the days, when graphics still used palettes instead of true color. A pixels colour wasn't set by giving it's RGB values, but by a single number offsetting into a limited palette of colours. Palette colours can still be found in several graphics file formats, but no decent piece of hardware uses them anymore.

Please erase glIndexPointer (and glIndex) from your memory and your code, they don't do what you think they do The whole indexed color mode is arcane to used, and frankly I don't know of any hardware built after 1998 that still supported it.

这篇关于计算三角形网格中的法线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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