Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating normals in a triangle mesh

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(); } //*************************************************************************************************************************** 
like image 798
Vince Avatar asked Jul 11 '11 20:07

Vince


People also ask

How do you find the normal of a triangle?

A surface normal for a triangle can be calculated by taking the vector cross product of two edges of that triangle. The order of the vertices used in the calculation will affect the direction of the normal (in or out of the face w.r.t. winding).

What are normals in a 3D mesh?

A normal in 3D modeling is a depiction of the orientation of a polygon's surface. It's basically a perpendicular line jutting out from the plane. When you're dealing with a curve, you'll use the plane lying tangent to the point in question to find its normal.

How do you calculate normal?

The normal to the plane is given by the cross product n=(r−b)×(s−b).


1 Answers

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.

like image 86
datenwolf Avatar answered Sep 24 '22 02:09

datenwolf