Vertex Normals, I think?
I am creating a terrain engine for a game my friend and I are making, and I am having trouble with lighting. When I set up some GL-lighting, the terrain dosn''t get shade at all, it is just one solid colour.
I not sure what it is that I have to do do acheive proper shading, I think I have to calcualte the vertex normals, but am not sure what they are, or how do calculate them.
Could someone please explain what I must do, point me in the right direction, or give me some good tutorials dealing with this matter?
Thanks
Ok mate - you have come across one of the more interesting points about 3d rendering. How on earth do you do lighting? Well - I''ll give you a brief overview here but do a search on the net for the information you need.
Ok there are 2 ways to do realtime lighting (Well there''s probably more but I''m only going to talk about 2
). The first way is to use something called a lightmap. A lightmap is literally just a texture which is white in the middle, fades out to black towards the edges and is blended onto the geometry in such a way that it looks as though it is lighting it up. Simple. Now what you are talking about is vertex lighting.
Vertex lighting, ok, you have a cube. Now vertex lighting works by colouring vertices. Depending on how close the vertex is to the light, the vertex''s colour is altered depending on the light''s own colour and its brightness etc. However, how do we even know if a face is pointed towards the light? And how do we tell whether the face is pointed straight at the light or is it slanted? (Remember, the more a face is looking away from the light, the darker it has to be). The answer is in normals. A normal is basically a vertex which is one unit long and which points out directly from the surface:
(Side view of surface)
| - Normal
----------- - Surface
By using some simple math it is possible using this special vector to tell both whether the surface is facing the light, and also at what angle it is pointed.
Just look it up on the net - there''s plenty of information out there!
Ok - once you know how to generate normals (They are -very- easy to generate from the vertices which make up an object) go bury your nose in the red book or the blue book (Links on NeHe front page) and look up lighting. Basically you have to enable lighting, then enable a light and define the lights properties.
Ok there are 2 ways to do realtime lighting (Well there''s probably more but I''m only going to talk about 2

Vertex lighting, ok, you have a cube. Now vertex lighting works by colouring vertices. Depending on how close the vertex is to the light, the vertex''s colour is altered depending on the light''s own colour and its brightness etc. However, how do we even know if a face is pointed towards the light? And how do we tell whether the face is pointed straight at the light or is it slanted? (Remember, the more a face is looking away from the light, the darker it has to be). The answer is in normals. A normal is basically a vertex which is one unit long and which points out directly from the surface:
(Side view of surface)
| - Normal
----------- - Surface
By using some simple math it is possible using this special vector to tell both whether the surface is facing the light, and also at what angle it is pointed.
Just look it up on the net - there''s plenty of information out there!
Ok - once you know how to generate normals (They are -very- easy to generate from the vertices which make up an object) go bury your nose in the red book or the blue book (Links on NeHe front page) and look up lighting. Basically you have to enable lighting, then enable a light and define the lights properties.
Just to expand on TheGilb a bit...
Within the 2nd method he mentions, vertex lighting, you again have 2 choices - you can either let OpenGL calculate the lighting for you, or do it yourself (which is increbily trendy these days, for some reason).
I''m going to show you the first option, letting OGL do it once you have the normals, as I don''t have the time to show you the other way right now.
As TheGilb said, a normal is a vector which is perpendicular to a plane - ie, it forms an L-shape with the plane.
Now, if we were calculating a normal for a polygon this would be easy to find, as the polygon (unless it''s a really awkward one) lies on a plane, and we can easily find a vector perpendicular to it. So how do we find a normal to a vertex when there''s no plane?
Well, we need to create the vector that faces "most away from" the vertex. First, we look at the edges that join the vertex. For example:
First, find vectors to represent the path from your current point to each of the others. To do this, simply subtract the position of each vertex from the current vertex. Then, normalise each vector.
Now, normalise both vectors and average them.
Now, if we take (averageX, averageY, averageZ) as a vector, let''s look at what that vector represents:
So, to get the vector facing "most away from" the vertex we''re looking at - ie, to get the normal - we simply invert that vector:
There''s no need to normalise the normal. We can guarantee it''s normalised if we normalised the vectors we averaged earlier.
So, to actually use our vector, just call glNormal3f right before you call glVertex3f, and remember to ad some light! For example:
Phew! Hope that helped. Lemme go for a cigarette and you''ll show you how to do the full lighting calculation yourself.
-=aSe=-
Within the 2nd method he mentions, vertex lighting, you again have 2 choices - you can either let OpenGL calculate the lighting for you, or do it yourself (which is increbily trendy these days, for some reason).
I''m going to show you the first option, letting OGL do it once you have the normals, as I don''t have the time to show you the other way right now.
As TheGilb said, a normal is a vector which is perpendicular to a plane - ie, it forms an L-shape with the plane.
Now, if we were calculating a normal for a polygon this would be easy to find, as the polygon (unless it''s a really awkward one) lies on a plane, and we can easily find a vector perpendicular to it. So how do we find a normal to a vertex when there''s no plane?
Well, we need to create the vector that faces "most away from" the vertex. First, we look at the edges that join the vertex. For example:
. The vertex we''re interested in (6, 6, 6) / \ / \ / \ / \ . . Two other vertices that join this one - (4, 4, 6) and (8, 4, 6)
First, find vectors to represent the path from your current point to each of the others. To do this, simply subtract the position of each vertex from the current vertex. Then, normalise each vector.
|
Now, normalise both vectors and average them.
|
Now, if we take (averageX, averageY, averageZ) as a vector, let''s look at what that vector represents:
. The vertex we''re working on /|\ / | \ / | \ / | \ . | . V - This is the vector we''ve created
So, to get the vector facing "most away from" the vertex we''re looking at - ie, to get the normal - we simply invert that vector:
|
There''s no need to normalise the normal. We can guarantee it''s normalised if we normalised the vectors we averaged earlier.
So, to actually use our vector, just call glNormal3f right before you call glVertex3f, and remember to ad some light! For example:
|
Phew! Hope that helped. Lemme go for a cigarette and you''ll show you how to do the full lighting calculation yourself.
-=aSe=-
-=aSe=-
Oh, just to clarify what we end up with at the end of the last bit:
And obviously I meant I''d tell you how to do it yourself instead, not you; can''t be bothered editing the post.
OK, so while OGL calculates the light values perfectly well on its own when you tell it the normals with glNormal3f, let''s look at what it actually does with them, since we can then do it ourself - which is cool.
Lambert was a clever bloke. He said that the apparent brightness of a given point to the viewer was dependant on the angles between its normal and the light source, like so:
We can easily calculate the vector representing the path from the point in question to the lightsource using the same technique we used earlier to find the vector representing the path to the neighbouring vertices, remembering to normalise them.
Once we have that, we can find the angle between the vectors very easily - this is given by
angle = inversecosine( U.V / (|U|*|V|))
Now, since we''re using normalised vectors we know that |U| (which represents the magnitude of vector U) is 1, as is |V|.
So we''ve simplified it to:
angle = inversecosine (U.V)
U.V is the dot product of U and V. So, to find that angle:
So now we have this angle, what do we do with it? Well, that angle is really our measure of the illumination of the point in question.
We can convert that angle to a shade of white, representing a brightness, and call glColor4f instead of glNormal3f before we call glVertex3f - since OGL interpolates the colours across a polygon (remember Nehe''s Lesson 3?), when we do this on textured polys it''ll effectively be interpolating brightness across the polygon!
But why stop there? For great, fast shadows, see if you have any objects lying between the point you''re testing and the light source - remember we know the vector that gets us there - and if you do, set glColor4f (0, 0, 0, 0) for black! Or better yet, use the actual colour of the object in the way of the light, and multiply it by the transparency of the object! Instant stained glass! The list goes on and on!
There is more to consider - such as the angle of the viewer - but if you''ve understand me this far, I really recommend you go off and write a raytracer, which will teach you everything you need to know to do really cool custom lighting in OGL, including wonderful fast shadows that don''t use the stencil buffer.
It''s a great little thing to program to take time out from OGL - and when you come back to OGL so many of the cutting edge techniques just fall into place for you.
HTH,
-=aSe=-
^ | - This is the normal! | | . The vertex we''re interested in (6, 6, 6) / \ / \ / \ / \ . .
And obviously I meant I''d tell you how to do it yourself instead, not you; can''t be bothered editing the post.
OK, so while OGL calculates the light values perfectly well on its own when you tell it the normals with glNormal3f, let''s look at what it actually does with them, since we can then do it ourself - which is cool.
Lambert was a clever bloke. He said that the apparent brightness of a given point to the viewer was dependant on the angles between its normal and the light source, like so:
. Lightsource \ \ ^ \ | \ | Normal \ | \ | \|----------.---- Surface
We can easily calculate the vector representing the path from the point in question to the lightsource using the same technique we used earlier to find the vector representing the path to the neighbouring vertices, remembering to normalise them.
Once we have that, we can find the angle between the vectors very easily - this is given by
angle = inversecosine( U.V / (|U|*|V|))
Now, since we''re using normalised vectors we know that |U| (which represents the magnitude of vector U) is 1, as is |V|.
So we''ve simplified it to:
angle = inversecosine (U.V)
U.V is the dot product of U and V. So, to find that angle:
|
So now we have this angle, what do we do with it? Well, that angle is really our measure of the illumination of the point in question.
We can convert that angle to a shade of white, representing a brightness, and call glColor4f instead of glNormal3f before we call glVertex3f - since OGL interpolates the colours across a polygon (remember Nehe''s Lesson 3?), when we do this on textured polys it''ll effectively be interpolating brightness across the polygon!
But why stop there? For great, fast shadows, see if you have any objects lying between the point you''re testing and the light source - remember we know the vector that gets us there - and if you do, set glColor4f (0, 0, 0, 0) for black! Or better yet, use the actual colour of the object in the way of the light, and multiply it by the transparency of the object! Instant stained glass! The list goes on and on!
There is more to consider - such as the angle of the viewer - but if you''ve understand me this far, I really recommend you go off and write a raytracer, which will teach you everything you need to know to do really cool custom lighting in OGL, including wonderful fast shadows that don''t use the stencil buffer.
It''s a great little thing to program to take time out from OGL - and when you come back to OGL so many of the cutting edge techniques just fall into place for you.
HTH,
-=aSe=-
-=aSe=-
http://home.clara.net/paulyg.
I had trouble coz MS3D''s file format screws up the vertex normals, thus screwing up the lighting, QED
. Try the above link. It has an MS3D loader and it calculates its own normals. Check out the code. Or check out aSe and TheGilb''s explanations. I don''t understand the math of it, but I understand the code. I just learn that that is the way to do it. That''s sometimes the easiest thing to do.
I had trouble coz MS3D''s file format screws up the vertex normals, thus screwing up the lighting, QED

October 24, 2001 02:21 PM
Hi.
I''ve just started writing a Vertex Normal Tutorial but I haven''t got a reply from NeHe yet. Maybe I will finish it next week and then it''s up to NeHe. Mail me if you want the code right away.
MVH / Erik
I''ve just started writing a Vertex Normal Tutorial but I haven''t got a reply from NeHe yet. Maybe I will finish it next week and then it''s up to NeHe. Mail me if you want the code right away.
MVH / Erik
October 24, 2001 05:45 PM
Wow,
Thanks for all the great responses! I didn''t expect such a great explanation =)
Thanks for all the great responses! I didn''t expect such a great explanation =)
One question though, I just tried on paper using the example you gave of (6,6,6),(4,4,6),(8,4,6) and it dosn''t work properly.
are you sure that this part is right?
becuase the equation above the output is
newVector1_x = 2
newVector1_y = 2
newVector1_z = 0
newVector2_x = -2
newVector2_y = 2
newVecotr2_z = 0
becuase later on you would get a division by zero becuase of the -2 + 2 = 0
and also shouldn''t
be
just a typo i think =)
Thanks
are you sure that this part is right?
|
becuase the equation above the output is
newVector1_x = 2
newVector1_y = 2
newVector1_z = 0
newVector2_x = -2
newVector2_y = 2
newVecotr2_z = 0
becuase later on you would get a division by zero becuase of the -2 + 2 = 0
and also shouldn''t
|
be
|
just a typo i think =)
Thanks
Hi
Maybe I have misunderstood your explanation aSe... but to calculate vertex normal I just take
a vertex and find all faces that share this vertex. Add all these faces normals together and divide by the number
of faces...now you have that vertex normal.
Another thing...how good is are those shadows you get from using that method you described?...
you need to have a lot of vertices to get a good result I guess. I think using lightmaps is a much better choice,
they only work for static shadows but they can look really great.
Edited by - RZ on October 25, 2001 2:33:06 PM
Maybe I have misunderstood your explanation aSe... but to calculate vertex normal I just take
a vertex and find all faces that share this vertex. Add all these faces normals together and divide by the number
of faces...now you have that vertex normal.
Another thing...how good is are those shadows you get from using that method you described?...
you need to have a lot of vertices to get a good result I guess. I think using lightmaps is a much better choice,
they only work for static shadows but they can look really great.
Edited by - RZ on October 25, 2001 2:33:06 PM
Thats how you do it.
Be careful though not to swap edges in the cross-product since you will get inverted results on one or more axises then. My advice is to make a vector class which can perform all vector operations, things like this will be a breeze once you know the theory behind it. My normal calculation function is only about 7 or 8 rows long thanks to my vector class.
BTW the inverse cosine function is acos(double a)
Be careful though not to swap edges in the cross-product since you will get inverted results on one or more axises then. My advice is to make a vector class which can perform all vector operations, things like this will be a breeze once you know the theory behind it. My normal calculation function is only about 7 or 8 rows long thanks to my vector class.
BTW the inverse cosine function is acos(double a)
Snale+--My humble and superior homepage
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement