Jump to content
  • Advertisement
Sign in to follow this  
d h k

Calculating Vertex-Normals [Solved]

This topic is 4659 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I have a terrain (heightmap) and the standard OGL lighting in place. It works fine, but now I want to switch away from using face normals and start to use vertex normals (I believe that's how the "better" ones are called, right?). I am talking about the normals, that will actually give a blurred look, and not this flat, blocky look: I do use this function to calculate my face normals:
void get_face_normal ( float *norm, float pointa[3], float pointb[3], float pointc[3] )
// gets the normal of a face
{
	float vect[2][3];
	int a,b;
	float point[3][3];

	for ( a = 0; a < 3; ++a )
	// copies points into point[][]
	{
		point[0][a]=pointa[a];
		point[1][a]=pointb[a]; 
		point[2][a]=pointc[a];
	}

	for ( a = 0; a < 2; ++a )
	// calculates vectors from point[0] to point[1]
	{                       
		for ( b = 0; b < 3; ++b )
		// and point[0] to point[2]
		{
			vect[a] = point[2-a] - point[0];      
		}
	}

	// calculates vector at 90° to 2 vectors
	cross_product ( norm, vect[0], vect[1] );

	// makes the vector length 1
	normalize ( norm );
}






Now how would I best move on to using those vertex normals? I know that I'll have three normals for one polygon then and that I somehow need to average something, but nothing more. Heck, I don't even know if "vertex normals" is the correct word... Any help/links to articles would be great! [Edited by - d h k on November 6, 2005 10:49:00 AM]

Share this post


Link to post
Share on other sites
Advertisement
There're simple ways to do so, and there are accurate ways. However, look at this www.gamedev.net/community/forums/topic.asp?topic_id=354136 thread. It has started 5 days ago and discusses several ways w/ pros and cons on computing vertex normals.

The simple way is to average the normals of all faces adjacent to the vertex. But also here meanings exist of whether or not, and if, how to weight the normals in dependence to the participation of the face to the vertex.

Quote:
Orignal post by d h k
Heck, I don't even know if "vertex normals" is the correct word...

Yes, "vertex normal" is a valid term for what you mean.

Share this post


Link to post
Share on other sites
Thanks for your help. I was hoping for an easier way, as the thread seems to be quite... advanced. ;)

Anyways, is there any other tutorial/code snippet than the "Normal Computations For A Heightfield" out there? This one confuses me with all those DirectX terms. I can't seem to find anymore.

Share this post


Link to post
Share on other sites
Quote:
Original post by d h k
Thanks for your help. I was hoping for an easier way, as the thread seems to be quite... advanced. ;)

Anyways, is there any other tutorial/code snippet than the "Normal Computations For A Heightfield" out there? This one confuses me with all those DirectX terms. I can't seem to find anymore.


In theory, getting the normals you want is very easy. The 'algorithm' is as follows:
- for every vertex in your model:
- sum the face normals (component wise) for avery triangle containing the vertex
- devide the summed normal (component wise) by the number of triangles containing the vertex to get the normal for the vertex

And that's it. Determining the list of triangles that contain a specific vertex can be tricky if your datatstructure does not support it (in that case you should probably redesign the way in which your terrain data is stored).

Tom

Share this post


Link to post
Share on other sites
Thanks for your help. I understand what you mean, but am having troubles actually implementing it...


for ( int x = 0; x &lt; ( MAP_SIZE - STEP_SIZE ); x += STEP_SIZE )
// loop through horizontal data
{
for ( int z = 0; z &lt; ( MAP_SIZE - STEP_SIZE ); z += STEP_SIZE )
// loop through vertical data
{
float vertex1[3];
float vertex2[3];
float vertex3[3];
float vertex4[3];
float normal1[3];
float normal2[3];
float normal3[3];
float normal4[3];

// get vertex data
vertex1[0] = ( float ) x;
vertex1[1] = get_height ( ( float ) x, ( float ) z );
vertex1[2] = ( float ) z;
vertex2[0] = ( float ) x;
vertex2[1] = get_height ( ( float ) x, ( float ) z + STEP_SIZE );
vertex2[2] = ( float ) z + STEP_SIZE;
vertex3[0] = ( float ) x + STEP_SIZE;
vertex3[1] = get_height ( ( float ) x + STEP_SIZE, ( float ) z + STEP_SIZE );
vertex3[2] = ( float ) z + STEP_SIZE;
vertex4[0] = ( float ) x + STEP_SIZE;
vertex4[1] = get_height ( ( float ) x + STEP_SIZE, ( float ) z );
vertex4[2] = ( float ) z;

// calculate normals
get_face_normal ( normal1, vertex1, vertex2, vertex3 );
get_face_normal ( normal2, vertex1, vertex3, vertex4 );

normal1[0] *= -1;
normal1[1] *= -1;
normal1[2] *= -1;
normal2[0] *= -1;
normal2[1] *= -1;
normal2[2] *= -1;

// draw the first triangle
glNormal3f ( normal1[0], normal1[1], normal1[2] );
glTexCoord2f ( 0.0f, 0.0f );
glVertex3f ( vertex1[0], vertex1[1], vertex1[2] );
glTexCoord2f ( 1.0f, 0.0f );
glVertex3f ( vertex2[0], vertex2[1], vertex2[2] );
glTexCoord2f ( 1.0f, 1.0f );
glVertex3f ( vertex3[0], vertex3[1], vertex3[2] );

// draw the second triangle
glNormal3f ( normal2[0], normal2[1], normal2[2] );
glTexCoord2f ( 0.0f, 0.0f );
glVertex3f ( vertex1[0], vertex1[1], vertex1[2] );
glTexCoord2f ( 1.0f, 0.0f );
glVertex3f ( vertex3[0], vertex3[1], vertex3[2] );
glTexCoord2f ( 0.0f, 1.0f );
glVertex3f ( vertex4[0], vertex4[1], vertex4[2] );
}
}



This is what I have to draw my terrain. So I need to calculate normal1 at vertex1, normal2 at vertex2, normal3 at vertex3 and normal4 at vertex4, right? I am quite confused at the moment, sorry for that. Could anybody explain just a little more?

Share this post


Link to post
Share on other sites
Quote:
Original post by d h k
Thanks for your help. I understand what you mean, but am having troubles actually implementing it...

*** Source Snippet Removed ***

This is what I have to draw my terrain. So I need to calculate normal1 at vertex1, normal2 at vertex2, normal3 at vertex3 and normal4 at vertex4, right? I am quite confused at the moment, sorry for that. Could anybody explain just a little more?

No problem :)
Your assumption is not exactly correct. Look at the picture from the thread linked by heagarr:


As you can see, each cross-point has a single normal, eventhough such a point is shared by 6 triangles. From your screenshot, it looks that your surface is similar, in the sense that each vertex is part of 6 triangles. Looking through your drawing code, it seems you draw 2 triangles at a time. That's probably where your confusion comes from, because in that function you don't have the information of the other 4 triangles that is required to calculate the vertex normals.

To solve your problem, you'll probably have to pre-calculate the vertex normals, because doing this during the rendering phase is inconvenient (and not too good for performance either). To achieve this, I suggest you create a datastructure that contains vertex, normal, texturecoordinate and connectivity data (which vertices belong to which triangles and vice versa) and a function that draws such a structure using OpenGL. This datastructure will make it easier to calculate the desired vertex normals and can be re-used for all your other models as well (assuming that eventually you want something to populate your empty terrain :).

EDIT: Combining an OpenGL VBO with the connectivity data required to calculate the normals, would be an efficient choice for this datastructure.

Tom

Share this post


Link to post
Share on other sites
The simplest algorithm is as follows:

For each vertex, find the faces adjacent to it. For each of that faces get the face normal (e.g. by cross product computation). Summarize the normals of all of those faces, and re-normalize the sum. So you've computed the simple average of all normals.

When rendering, push the belonging vertex normal just before pushing the vertex point (in a pseude-code)

for each vertex v do {
VertexNormal n = { 0,0,0 };
for each face f adjacent to v do {
n += f.getNormal();
}
n.normalize();
glNormal(n);
glVertex3f(v.point());
}

Share this post


Link to post
Share on other sites
Thanks a lot to both of you, I appreciate it, especially since terrain is going to be an important part of my project. I'll implement it right away.

Share this post


Link to post
Share on other sites
When most model formats export data, they do so like this for each polygon:
numsides vertex1id vertex2id vertex3id ...
Anyway, given this, the easiest approach would be not to look for faces using a vertex, but to do the following:

1. Read the faces in your model file
2. Calculate the face normal
3. Add it to the normal of each vertex making up that face (if you look at the example line above, this is really easy)
4. After that's done, normalize all the vertex normals

So you don't have to go looking for faces which share a vertex at all. I personally found this approach much easier to implement.

Share this post


Link to post
Share on other sites
i am going to go ahead and post my vertex normals function that i use. at the offset i should advise that you create a normals class to do all your stuff.. such as cross product and so forth. in some screenshots that i have posted in the past such as here and here , you can see the work of vertex normals. since ill assume you know how do cross product and all that stuff i will just go ahead and give you sample code with a simple data structure. hope this helps!

//class.h i define a few variables of type data

struct data
{
double x;
double y;
double z;
};

data *terrain;
data *normals





//in class.cpp i calculate what my xsize and ysize will be then allocate memory equally for my terrain and normals, then i store the x,y,z pairs in my terrain array. i have found its also helpful when storing your data to calculate the number of vertices you will have total which you can get by x_size * y_size * 3. you can read what your x,y & z values are through a file on disk or if your values are predefined you can do it manually.

terrain = new data[x_size * y_size * 3];
normals = new data[x_size * y_size * 3];

for(int i = 0; i < y_size; i++)
{
for(int j = 0; j < x_size; j++)
terrain[count].x = num_x; terrain[count].y = num_y; terrain[count].z = num_z;
}





//in class.cpp you can call a load normals function that will load the normals in per vertex. i have a CVector class that is included in a header file that instantiates variables in the class for doing cross product, subtraction and addition of vectors as well as many other things. you can see some of them at work here.


void load_normals();
{
int count;
CVector V4;
CVector V5;
CVector V6;

for(int a = 0; a < ysize; a++)
{
for(int b = 0; b < x_size; b++)
{
if(count < x_size)
{
CVector V2(terrain[((a+1)*x_size)+b+1].x, terrain[((a+1)*x_size)+b+1].y, terrain[((a+1)*x_size)+b+1].z);
CVector V1(terrain[((a+1)*x_size)+b].x, terrain[((a+1)*x_size)+b].y, terrain[((a+1)*x_size)+b].z);

V4 = V2 - V1;

CVector V0(terrain[((a)*x_size)+b].x, terrain[((a)*x_size)+b].y, terrain[((a)*x_size)+b].z);
CVector V3(V1);

V5 = V0 - V3;

count++;
}

else if(count == x_size)
{

CVector V2(terrain[((a)*x_size)+b-1].x, terrain[((a)*x_size)+b-1].y, terrain[((a)*x_size)+b-1].z);
CVector V1(terrain[((a)*x_size)+b].x, terrain[((a)*x_size+b].y, terrain[((a)*x_size)+b].z);

V4 = V2 - V1;

CVector V0(terrain[((a+1)*x_size)+b].x, terrain[((a+1)*x_size)+b].y, terrain[((a+1)*x_size)+b].z);
CVector V3(V1);

V5 = V0 - V3;

count = 0;
}


V6 = V5.Cross(V4); //cross product
V6.normalize(); //normailizing to the unit vector = 1

//now lets store the normals after calculating them
normals[(a*x_size)+b].x = -V6.x;
normals[(a*x_size)+b].y = -V6.y;
normals[(a*x_size)+b].z = -V6.z;
} //end b

} //end a

} //end load_normals()






now you have your terrain and normals stored and you are set up to draw your stuff. obviously my code could be lengthed by doing extra checking among all the vertices, to have some slight better results in the shading between vertices, but this works pretty well and i hope you understand it. so now you are read to do:

glNormal3f(normals[count].x, normals[count].y, normals[count].z)
glVertex3f(terrain[count].x, terrain[count].y, terrain[count].z)

or just however you decide to do this part.

again i hope this helps and i welcome comments!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!