计算三角形网格中的法线

时间:2023-02-09 09:02:19

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?

我画了一个有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();
}

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

3 个解决方案

#1


90  

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.

像往常一样,答案是:“看情况而定”。由于法向量定义为垂直于给定平面(N维)内所有向量的向量,所以需要一个平面来计算法向量。顶点位置只是一个点,因此你需要一张脸来计算正常。因此,我们可以天真地假设,法线是每个面,正常计算的第一步是通过计算面边的叉乘来确定面法线。

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)

说你有一个三角形,点a,B,C,那么这些点位置向量↑↑B,C和边缘有向量↑↑B - a和C↑↑↑面法向量是↑Nf =(↑B -↑)×(C -↑↑)

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

注意,上述↑Nf的级成正比的脸的区域。

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.

↑↑Nv =∑p Nf;p是每个面的权重。

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.

现在回想一下,你正常的向量↑v通过扩展recipocal长度:↑vi =↑v / | |↑v。但就像已经告诉过的脸的长度一般已经取决于脸的面积。所以上面给出的加权因子p已经包含在向量本身中:它的长度,也就是大小。我们可以得到顶点法向量只要把所有的面法线相加。

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

细心的读者可能已经注意到我特别说过光滑的表面共享顶点。事实上,如果你的几何图形中有一些折痕/硬边,那么两边的面就不会共享顶点。在OpenGL中,一个顶点是整个的组合。

  • position
  • 位置
  • normal
  • 正常的
  • (colour)
  • (颜色)
  • N texture coordinates
  • N纹理坐标
  • M further attributes
  • M进一步的属性

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.

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


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.

索引指针与顶点数组索引无关!这是一个过去的错误,那时的图形仍然使用调色板而不是真正的颜色。像素颜色不是通过给出RGB值来设置的,而是通过一个数字来抵消有限的颜色。调色板颜色仍然可以在几种图形文件格式中找到,但是再也没有合适的硬件使用它们了。

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.

请将glIndexPointer(和glIndex)从您的内存和代码中删除,它们不会做您认为它们会做的事情。

#2


18  

Per-vertex.

种每个顶点都具备的。

Use cross-products to calculate the face normals for the triangles surrounding a given vertex, add them together, and normalize.

使用交叉乘积来计算给定顶点周围三角形的面法线,将它们相加,并进行规范化。

#3


15  

Thumbs up for datenwolf! I completely agree with his approach. Adding the normal vectors of the adjacent triangles for each vertex and then normalising is the way to go. I just want to push the answer a little bit and have a closer look at the particular but quite common case of a rectangular, smooth mesh that has a constant x/y step. In other words, a rectangular x/y grid with a variable height at each point.

大拇指datenwolf !我完全同意他的方法。将每个顶点的邻边三角形的法向量相加,然后进行归一化。我只是想把答案稍微推一下,仔细看看这个特殊但很普通的矩形,光滑的网格,它有一个恒定的x/y步。换句话说,一个矩形的x/y网格,每个点的高度都是可变的。

Such a mesh is created by looping over x and y and setting a value for z and can represent things like the surface of a hill. So each point of the mesh is represented by a vector

这样的网格是通过在x和y上循环并为z设置一个值来创建的,它可以表示像小山的表面这样的东西。所以网格的每个点都由一个向量表示。

P = (x, y, f(x,y)) 

where f(x,y) is a function giving the z of each point on the grid.

其中f(x,y)是一个函数,给出网格上每个点的z。

Usually to draw such a mesh we use a TriangleStrip or a TriangleFan but any technique should give a similar topography for the resulting triangles.

通常为了绘制这样的网格,我们使用三角形带或三角形扇,但是任何技术都应该为生成的三角形提供类似的地形。

     |/   |/   |/   |/
...--+----U----UR---+--...
    /|   /| 2 /|   /|           Y
   / |  / |  / |  / |           ^
     | /  | /  | /  | /         |
     |/ 1 |/ 3 |/   |/          |
...--L----P----R----+--...      +-----> X
    /| 6 /| 4 /|   /|          
   / |  / |  / |  / |         
     | /5 | /  | /  | /      
     |/   |/   |/   |/
...--DL---D----+----+--...
    /|   /|   /|   /|

For a triangleStrip each vertex P=(x0, y0, z0) has 6 adjacent vertices denoted

对于三角形带,每个顶点P=(x0, y0, z0)有6个相邻顶点

up       = (x0     , y0 + ay, Zup)
upright  = (x0 + ax, y0 + ay, Zupright) 
right    = (x0 + ax, y0     , Zright) 
down     = (x0     , y0 - ay, Zdown)
downleft = (x0 - ax, y0 - ay, Zdownleft) 
left     = (x0 - ax, y0     , Zleft)

where ax/ay is the constant grid step on the x/y axis respectively. On a square grid ax = ay.

其中ax/ay分别是x/y轴上的恒定网格步。在正方形网格上ax = ay。

ax = width / (nColumns - 1)
ay = height / (nRows - 1)

Thus each vertex has 6 adjacent triangles each one with its own normal vector (denoted N1 to N6). These can be calculated using the cross product of the two vectors defining the side of the triangle and being careful on the order in which we do the cross product. If the normal vector points in the Z direction towards you :

因此每个顶点都有6个相邻的三角形,每个三角形都有自己的法向量(表示N1到N6)。这些可以通过定义三角形边的两个向量的外积来计算并且要注意我们做外积的顺序。如果法向量指向你的Z方向:

N1 = up x left =
   = (Yup*Zleft - Yleft*Zup, Xleft*Zup - Xup*ZLeft, Xleft*Yup - Yleft*Xup) 

   =( (y0 + ay)*Zleft - y0*Zup, 
      (x0 - ax)*Zup   - x0*Zleft, 
      x0*y0 - (y0 + ay)*(x0 - ax) ) 

N2 = upright  x up
N3 = right    x upright
N4 = down     x right
N5 = downleft x down
N6 = left     x downleft

And the resulting normal vector for each point P is the sum of N1 to N6. We normalise after summing. It's very easy to create a loop, calculate the values of each normal vector, add them and then normalise. However, as pointed out by Mr. Shickadance, this can take quite a while, especially for large meshes and/or on embedded devices.

每个点P的法向量是N1到N6的和。求和后我们正常化。很容易创建一个循环,计算每个法向量的值,添加它们,然后规范化。然而,正如Shickadance先生指出的,这可能需要很长一段时间,特别是对于大型网格和/或嵌入式设备。

If we have a closer look and perform the calculations by hand, we will find out that most of the terms cancel out each other, leaving us with a very elegant and easy to calculate final solution for the resulting vector N. The point here is to speed up calculations by avoiding calculating the coordinates of N1 to N6, doing 6 cross-products and 6 additions for each point. Algebra helps us to jump straight to the solution, use less memory and less CPU time.

如果我们有一个近距离的观察,进行手工计算,我们将发现的大部分条款相互抵消了,留给我们一个非常优雅,很容易计算结果向量n最终解决方案的重点是加快计算通过避免计算N6 N1的坐标,为每个点做6得叉积6添加。代数帮助我们直接跳到解决方案,使用更少的内存和更少的CPU时间。

I will not show the details of the calculations as it is long but straight-forward and will jump to the final expression of the Normal vector for any point on the grid. Only N1 is decomposed for the sake of clarity, the other vectors look alike. After summing we obtain N which is not yet normalized :

我将不展示计算的细节,因为它很长但很直接,并且会跳转到网格上任意一点的法向量的最终表达式。为了清晰起见,只有N1被分解,其他向量看起来很相似。求和后,我们得到N,尚未归一化:

N = N1 + N2 + ... + N6

  = .... (long but easy algebra) ...

  = ( (2*(Zleft - Zright) - Zupright + Zdownleft + Zup - Zdown) / ax,
      (2*(Zdown - Zup)    + Zupright + Zdownleft - Zup - Zleft) / ay,
       6 )

There you go! Just normalise this vector and you have the normal vector for any point on the grid, provided you know the Z values of its surrounding points and the horizontal/vertical step of your grid.

你走吧!只要将这个向量标准化,就可以得到网格上任意点的法向量,只要你知道它周围点的Z值和网格的水平/垂直步。

Note that this is the weighed average of the surrounding triangles' normal vectors. The weight is the area of the triangles and is already included in the cross product.

注意,这是周围三角形法向量的加权平均值。权重是三角形的面积,并且已经包含在外积中。

You can even simplify it more by only taking into account the Z values of four surrounding points (up,down,left and right). In that case you get :

你甚至可以把它简化得更简单一些,只要考虑到周围四个点的Z值(向上、向下、左和右)。在这种情况下,你会得到:

                                             |   \|/   |
N = N1 + N2 + N3 + N4                    ..--+----U----+--..
  = ( (Zleft - Zright) / ax,                 |   /|\   |
      (Zdown -  Zup  ) / ay,                 |  / | \  |
       2 )                                 \ | / 1|2 \ | /
                                            \|/   |   \|/
                                         ..--L----P----R--...
                                            /|\   |   /|\
                                           / | \ 4|3 / | \
                                             |  \ | /  |
                                             |   \|/   |
                                         ..--+----D----+--..
                                             |   /|\   |

which is even more elegant and even faster to calculate.

它更优雅,计算速度更快。

Hope this will make some meshes faster. Cheers

希望这将使一些网格更快。干杯

#1


90  

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.

像往常一样,答案是:“看情况而定”。由于法向量定义为垂直于给定平面(N维)内所有向量的向量,所以需要一个平面来计算法向量。顶点位置只是一个点,因此你需要一张脸来计算正常。因此,我们可以天真地假设,法线是每个面,正常计算的第一步是通过计算面边的叉乘来确定面法线。

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)

说你有一个三角形,点a,B,C,那么这些点位置向量↑↑B,C和边缘有向量↑↑B - a和C↑↑↑面法向量是↑Nf =(↑B -↑)×(C -↑↑)

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

注意,上述↑Nf的级成正比的脸的区域。

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.

↑↑Nv =∑p Nf;p是每个面的权重。

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.

现在回想一下,你正常的向量↑v通过扩展recipocal长度:↑vi =↑v / | |↑v。但就像已经告诉过的脸的长度一般已经取决于脸的面积。所以上面给出的加权因子p已经包含在向量本身中:它的长度,也就是大小。我们可以得到顶点法向量只要把所有的面法线相加。

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

细心的读者可能已经注意到我特别说过光滑的表面共享顶点。事实上,如果你的几何图形中有一些折痕/硬边,那么两边的面就不会共享顶点。在OpenGL中,一个顶点是整个的组合。

  • position
  • 位置
  • normal
  • 正常的
  • (colour)
  • (颜色)
  • N texture coordinates
  • N纹理坐标
  • M further attributes
  • M进一步的属性

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.

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


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.

索引指针与顶点数组索引无关!这是一个过去的错误,那时的图形仍然使用调色板而不是真正的颜色。像素颜色不是通过给出RGB值来设置的,而是通过一个数字来抵消有限的颜色。调色板颜色仍然可以在几种图形文件格式中找到,但是再也没有合适的硬件使用它们了。

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.

请将glIndexPointer(和glIndex)从您的内存和代码中删除,它们不会做您认为它们会做的事情。

#2


18  

Per-vertex.

种每个顶点都具备的。

Use cross-products to calculate the face normals for the triangles surrounding a given vertex, add them together, and normalize.

使用交叉乘积来计算给定顶点周围三角形的面法线,将它们相加,并进行规范化。

#3


15  

Thumbs up for datenwolf! I completely agree with his approach. Adding the normal vectors of the adjacent triangles for each vertex and then normalising is the way to go. I just want to push the answer a little bit and have a closer look at the particular but quite common case of a rectangular, smooth mesh that has a constant x/y step. In other words, a rectangular x/y grid with a variable height at each point.

大拇指datenwolf !我完全同意他的方法。将每个顶点的邻边三角形的法向量相加,然后进行归一化。我只是想把答案稍微推一下,仔细看看这个特殊但很普通的矩形,光滑的网格,它有一个恒定的x/y步。换句话说,一个矩形的x/y网格,每个点的高度都是可变的。

Such a mesh is created by looping over x and y and setting a value for z and can represent things like the surface of a hill. So each point of the mesh is represented by a vector

这样的网格是通过在x和y上循环并为z设置一个值来创建的,它可以表示像小山的表面这样的东西。所以网格的每个点都由一个向量表示。

P = (x, y, f(x,y)) 

where f(x,y) is a function giving the z of each point on the grid.

其中f(x,y)是一个函数,给出网格上每个点的z。

Usually to draw such a mesh we use a TriangleStrip or a TriangleFan but any technique should give a similar topography for the resulting triangles.

通常为了绘制这样的网格,我们使用三角形带或三角形扇,但是任何技术都应该为生成的三角形提供类似的地形。

     |/   |/   |/   |/
...--+----U----UR---+--...
    /|   /| 2 /|   /|           Y
   / |  / |  / |  / |           ^
     | /  | /  | /  | /         |
     |/ 1 |/ 3 |/   |/          |
...--L----P----R----+--...      +-----> X
    /| 6 /| 4 /|   /|          
   / |  / |  / |  / |         
     | /5 | /  | /  | /      
     |/   |/   |/   |/
...--DL---D----+----+--...
    /|   /|   /|   /|

For a triangleStrip each vertex P=(x0, y0, z0) has 6 adjacent vertices denoted

对于三角形带,每个顶点P=(x0, y0, z0)有6个相邻顶点

up       = (x0     , y0 + ay, Zup)
upright  = (x0 + ax, y0 + ay, Zupright) 
right    = (x0 + ax, y0     , Zright) 
down     = (x0     , y0 - ay, Zdown)
downleft = (x0 - ax, y0 - ay, Zdownleft) 
left     = (x0 - ax, y0     , Zleft)

where ax/ay is the constant grid step on the x/y axis respectively. On a square grid ax = ay.

其中ax/ay分别是x/y轴上的恒定网格步。在正方形网格上ax = ay。

ax = width / (nColumns - 1)
ay = height / (nRows - 1)

Thus each vertex has 6 adjacent triangles each one with its own normal vector (denoted N1 to N6). These can be calculated using the cross product of the two vectors defining the side of the triangle and being careful on the order in which we do the cross product. If the normal vector points in the Z direction towards you :

因此每个顶点都有6个相邻的三角形,每个三角形都有自己的法向量(表示N1到N6)。这些可以通过定义三角形边的两个向量的外积来计算并且要注意我们做外积的顺序。如果法向量指向你的Z方向:

N1 = up x left =
   = (Yup*Zleft - Yleft*Zup, Xleft*Zup - Xup*ZLeft, Xleft*Yup - Yleft*Xup) 

   =( (y0 + ay)*Zleft - y0*Zup, 
      (x0 - ax)*Zup   - x0*Zleft, 
      x0*y0 - (y0 + ay)*(x0 - ax) ) 

N2 = upright  x up
N3 = right    x upright
N4 = down     x right
N5 = downleft x down
N6 = left     x downleft

And the resulting normal vector for each point P is the sum of N1 to N6. We normalise after summing. It's very easy to create a loop, calculate the values of each normal vector, add them and then normalise. However, as pointed out by Mr. Shickadance, this can take quite a while, especially for large meshes and/or on embedded devices.

每个点P的法向量是N1到N6的和。求和后我们正常化。很容易创建一个循环,计算每个法向量的值,添加它们,然后规范化。然而,正如Shickadance先生指出的,这可能需要很长一段时间,特别是对于大型网格和/或嵌入式设备。

If we have a closer look and perform the calculations by hand, we will find out that most of the terms cancel out each other, leaving us with a very elegant and easy to calculate final solution for the resulting vector N. The point here is to speed up calculations by avoiding calculating the coordinates of N1 to N6, doing 6 cross-products and 6 additions for each point. Algebra helps us to jump straight to the solution, use less memory and less CPU time.

如果我们有一个近距离的观察,进行手工计算,我们将发现的大部分条款相互抵消了,留给我们一个非常优雅,很容易计算结果向量n最终解决方案的重点是加快计算通过避免计算N6 N1的坐标,为每个点做6得叉积6添加。代数帮助我们直接跳到解决方案,使用更少的内存和更少的CPU时间。

I will not show the details of the calculations as it is long but straight-forward and will jump to the final expression of the Normal vector for any point on the grid. Only N1 is decomposed for the sake of clarity, the other vectors look alike. After summing we obtain N which is not yet normalized :

我将不展示计算的细节,因为它很长但很直接,并且会跳转到网格上任意一点的法向量的最终表达式。为了清晰起见,只有N1被分解,其他向量看起来很相似。求和后,我们得到N,尚未归一化:

N = N1 + N2 + ... + N6

  = .... (long but easy algebra) ...

  = ( (2*(Zleft - Zright) - Zupright + Zdownleft + Zup - Zdown) / ax,
      (2*(Zdown - Zup)    + Zupright + Zdownleft - Zup - Zleft) / ay,
       6 )

There you go! Just normalise this vector and you have the normal vector for any point on the grid, provided you know the Z values of its surrounding points and the horizontal/vertical step of your grid.

你走吧!只要将这个向量标准化,就可以得到网格上任意点的法向量,只要你知道它周围点的Z值和网格的水平/垂直步。

Note that this is the weighed average of the surrounding triangles' normal vectors. The weight is the area of the triangles and is already included in the cross product.

注意,这是周围三角形法向量的加权平均值。权重是三角形的面积,并且已经包含在外积中。

You can even simplify it more by only taking into account the Z values of four surrounding points (up,down,left and right). In that case you get :

你甚至可以把它简化得更简单一些,只要考虑到周围四个点的Z值(向上、向下、左和右)。在这种情况下,你会得到:

                                             |   \|/   |
N = N1 + N2 + N3 + N4                    ..--+----U----+--..
  = ( (Zleft - Zright) / ax,                 |   /|\   |
      (Zdown -  Zup  ) / ay,                 |  / | \  |
       2 )                                 \ | / 1|2 \ | /
                                            \|/   |   \|/
                                         ..--L----P----R--...
                                            /|\   |   /|\
                                           / | \ 4|3 / | \
                                             |  \ | /  |
                                             |   \|/   |
                                         ..--+----D----+--..
                                             |   /|\   |

which is even more elegant and even faster to calculate.

它更优雅,计算速度更快。

Hope this will make some meshes faster. Cheers

希望这将使一些网格更快。干杯