# Computing Face Normals (Updated with SCREENS)

## Recommended Posts

d h k    439
Hey there, I am trying to compute simple ("unsmooth") face normals. I followed some article on gamedev.net, but something isn't working (my objects don't appear brightened by any light). I am using OpenGL and I know that the lighting works. I know I could write a hack to display the computed normals and see what is actually wrong, but then again it may be faster if one of you professionals might just take a real quick look at my calculations and point out my mistakes... Here is the relevant code part: Some function definitions (cross_product, normalize and get_face_normal)...
void cross_product ( float *c,float a[3], float b[3] )
// finds the cross product of two vectors
{
c[0] = a[1] * b[2] - b[1] * a[2];
c[1] = a[2] * b[0] - b[2] * a[0];
c[2] = a[0] * b[1] - b[0] * a[1];
}

void normalize ( float * vect )
// scales a vector to a length of 1
{
float length;
int a;

length = ( float ) sqrt ( pow ( vect[0], 2 ) + pow ( vect[1], 2 ) + pow ( vect[2], 2 ) );

for ( a = 0; a < 3; ++a )
// divides vector by its length to normalize
{
vect[a] /= length;
}
}

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][b] = point[2-a][b] - point[0][b];
}
}

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

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


How I call the relevant function (get_face_normal)...
for ( int i = 0; i < p_object->num_polygons; i++ )
// loop through each polygon
{
float vertex1[3], vertex2[3], vertex3[3];

vertex1[0] = p_object->vertex[p_object->polygon[i].a ].x;
vertex1[1] = p_object->vertex[p_object->polygon[i].a ].y;
vertex1[2] = p_object->vertex[p_object->polygon[i].a ].z;
vertex2[0] = p_object->vertex[p_object->polygon[i].b ].x;
vertex2[1] = p_object->vertex[p_object->polygon[i].b ].y;
vertex2[2] = p_object->vertex[p_object->polygon[i].b ].z;
vertex3[0] = p_object->vertex[p_object->polygon[i].c ].x;
vertex3[1] = p_object->vertex[p_object->polygon[i].c ].y;
vertex3[2] = p_object->vertex[p_object->polygon[i].c ].z;

get_face_normal ( p_object->normal, vertex1, vertex2, vertex3 );
}


EDIT: When I move my light to exactly position 0, 0, 0 it lights up most of my polygons, but still some appear black. What the heck is wrong? :) I know that my vertexes data is correct, since I also use this data to draw my objects to the screen and they are drawn correctly. Thanks for your help. [Edited by - d h k on October 6, 2005 6:53:17 AM]

##### Share on other sites
SuperNerd    100
A polygon normal go in two directions. Try multiplying the X, Y, and Z of your normals by a -1 to flip it the other way.

##### Share on other sites
d h k    439
Thanks for your reply, now it looks a little better, but still the polygons only appear brightened at all when the light is close to the point 0, 0, 0. If it is slightly higher, some polygons are lighted, if it is slightly lower, then some other polygons are brightened.

When you move the light approx. ~0.5 units away from the point 0, 0, 0 in any direction, all faces turn black and unlightened...

What could still be wrong?

This is the new code and some more ( the function definitions are the same than before ):

for ( int i = 0; i < actor.num_polygons; i++ )    // loop through each polygon	{		float vertex1[3], vertex2[3], vertex3[3];		// prepare vertex data		vertex1[0] = actor.vertex[actor.polygon[i].a ].x;		vertex1[1] = actor.vertex[actor.polygon[i].a ].y;		vertex1[2] = actor.vertex[actor.polygon[i].a ].z;		vertex2[0] = actor.vertex[actor.polygon[i].b ].x;		vertex2[1] = actor.vertex[actor.polygon[i].b ].y;		vertex2[2] = actor.vertex[actor.polygon[i].b ].z;		vertex3[0] = actor.vertex[actor.polygon[i].c ].x;		vertex3[1] = actor.vertex[actor.polygon[i].c ].y;		vertex3[2] = actor.vertex[actor.polygon[i].c ].z;				// get the face normal		get_face_normal ( actor.normal, vertex1, vertex2, vertex3 );		// multiply it by -1		actor.normal[0] *= -1;		actor.normal[1] *= -1;		actor.normal[2] *= -1;		// begin drawing polygon		glBegin ( GL_TRIANGLES );				// activate face normal		glNormal3f ( actor.normal[0], actor.normal[1], actor.normal[2] );		// draw first vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].a].u, actor.mapcoord[actor.polygon[i].a].v );		glVertex3f ( actor.vertex[actor.polygon[i].a ].x, actor.vertex[actor.polygon[i].a ].y, actor.vertex[actor.polygon[i].a ].z );				// draw second vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].b].u, actor.mapcoord[actor.polygon[i].b].v );		glVertex3f ( actor.vertex[actor.polygon[i].b].x, actor.vertex[actor.polygon[i].b].y, actor.vertex[actor.polygon[i].b].z );		// draw third vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].c].u, actor.mapcoord[actor.polygon[i].c].v );		glVertex3f ( actor.vertex[actor.polygon[i].c].x, actor.vertex[actor.polygon[i].c].y, actor.vertex[actor.polygon[i].c].z );		// done drawing polygon		glEnd ( );    }

##### Share on other sites
d h k    439
Allright, in the hope that someone might please reply, I decided to add screenshots to explain myself and the problem a little better...

Situation 1
The light is located at 0, 0, 0 initially ( also where the origin of the particle system is for orientation ). All the faces are lit up correctly in this situation. The ground face is not brighten because the light's z position is exactly 0.

Situation 2
Now here's the problem: I moved the light up a very little bit ( now maybe at 0, 0, -0.5 and the particle system origin moved with the new light position, although the change may be so small, that you can't see it - I barely pushed the UP cursor key to move the light ). Now the ground face is lit correctly, but the faces on all objects ( see the weapon model ) are messed up. Also when I start rotatiing and moving the camera around the screen flickers black and lightened up ( red ).

Now when I move even further in any direction, everything turns black.

What is the problem? Please answer, this is a really important step for my project. Is there anything that is wrong with my normals calculation? Please also tell me if they are correct, so that I know I can look for the mistake somewhere else.

Thank you.

##### Share on other sites
sakana321    133
Are your light within viewing volume?

##### Share on other sites
sakana321    133
What kind of attenuation model are you using?

##### Share on other sites
sakana321    133
Are you try
glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_TRUE);
yet?

##### Share on other sites
d h k    439

I am not quite sure what you mean with your first two ones though:

My light should be in the viewing volume, as I have a standard camera setup and a light at 0, 0, 0.

Also, sorry to sound newbish (or is it noobish) here, but what is "kind-of-attenuation"?

Now to your third suggestion: I don't call that function, but when I try to call it on light initialization, it doesn't recognize it as OpenGL function (actually LIGHT_MODEL_TWO_SIDE is unknown)...

Could you please explain yourself more. Thanks!

EDIT: I looked the correct version up. GL_LIGHT_MODEL_TWO_SIDE doesn't change anything though!

##### Share on other sites
dimebolt    440
Quote:
 Original post by d h kEDIT: When I move my light to exactly position 0, 0, 0 it lights up most of my polygons, but still some appear black. What the heck is wrong? :)I know that my vertexes data is correct, since I also use this data to draw my objects to the screen and they are drawn correctly.Thanks for your help.

I didn't look for bugs in your code, but where do you get the models from? Are the triangles consistently clockwise or counter clockwise? If not, it is impossible to calculate the signs of the normals from triangle data alone.

It might also help if you post the lighting/material code. I'm curious what type of specular/diffuse/ambient/shininess you use.

Tom

##### Share on other sites
d h k    439

Simple things first: I have no idea why that second image isn't showing up. I uploaded it to rapidshare just like I did with the first screen, and the first one works... Here is a new link that works!

Now to the problem: Things look allright when I don't move the light ( it starts at 0, 0, 0 ). I can then move the camera around and do everything I want to and it all looks fine.

But when I move the light around, it suddenly starts to flicker and very soon all is black.

Here is more code, that you requested:

I initialize a light like this:
// initialize a lightfloat ambient[] = { 1.0f, 0.0f, 0.0f, 1.0f };float diffuse[] = { 1.0f, 0.0f, 0.0f, 1.0f };float specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };float position[] = { 0.0f, 0.0f, 0.0f };lgt.init ( ambient, diffuse, specular );lgt.set_position ( position );lgt.toggle ( true );

Those are the definitions of the functions used above:
void light::init ( float ambient[], float diffuse[], float specular[] )// initializes a light{	glLightfv ( GL_LIGHT1, GL_AMBIENT, ambient );	glLightfv ( GL_LIGHT1, GL_DIFFUSE, diffuse );	glLightfv ( GL_LIGHT1, GL_SPECULAR, specular );	glLightModeli ( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );	glEnable ( GL_LIGHT1 );}void light::toggle ( void )// toggles a light on or off{	if ( mode == true )	// if light is activated	{		glDisable ( GL_LIGHT1 );		mode = false;	}	else	// if light is deactivated	{		glEnable ( GL_LIGHT1 );		mode = true;	}}void light::toggle ( bool new_mode )// switch a light to a specific state{	mode = new_mode;	if ( mode == true )	// if light is activated		glEnable ( GL_LIGHT1 );	else	// if light is activated		glDisable ( GL_LIGHT1 );}void light::set_position ( float light_position[] )// updates the position of the light{	// reset the matrix	glLoadIdentity ( );	// position the camera	cam.update ( );	// move the light	glLightfv ( GL_LIGHT1, GL_POSITION, light_position );}

Here is my object::draw ( ) function, that draws models from 3ds files:
[source lang="cpp]void object::draw ( )// draws the object normally to the screen{	// reset the matrix	glLoadIdentity ( );	// position the camera	cam.update ( );	// update acceleration values	update_position ( );	// move and rotate to place the object	glTranslatef ( position.x, position.y, position.z );	glRotatef ( rotation.x, 1.0f, 0.0f, 0.0f );	glRotatef ( rotation.y, 0.0f, 1.0f, 0.0f );	glRotatef ( rotation.z, 0.0f, 0.0f, 1.0f );	// set up render modes	glColorMask ( 1, 1, 1, 1 );	glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f );	glDisable ( GL_BLEND );	glDisable ( GL_CLIP_PLANE0 );	glEnable ( GL_DEPTH_TEST );	glDisable ( GL_STENCIL_TEST );	if ( sphere_mapped == true )	// if the object is sphere mapped	{		// enable sphere mapping		glEnable ( GL_TEXTURE_GEN_S );		glEnable ( GL_TEXTURE_GEN_T );	}	else	// if the object is texture mapped	{		// disable shere mapping		glDisable ( GL_TEXTURE_GEN_S );		glDisable ( GL_TEXTURE_GEN_T );	}	// activate texture	surface_texture.activate ( );	// push the matrix	glPushMatrix ( );	// scale	glScalef ( size, size, size );	for ( int i = 0; i < actor.num_polygons; i++ )    // loop through each polygon	{		float vertex1[3], vertex2[3], vertex3[3];		// prepare vertex data		vertex1[0] = actor.vertex[actor.polygon[i].a ].x;		vertex1[1] = actor.vertex[actor.polygon[i].a ].y;		vertex1[2] = actor.vertex[actor.polygon[i].a ].z;		vertex2[0] = actor.vertex[actor.polygon[i].b ].x;		vertex2[1] = actor.vertex[actor.polygon[i].b ].y;		vertex2[2] = actor.vertex[actor.polygon[i].b ].z;		vertex3[0] = actor.vertex[actor.polygon[i].c ].x;		vertex3[1] = actor.vertex[actor.polygon[i].c ].y;		vertex3[2] = actor.vertex[actor.polygon[i].c ].z;				// get the face normal		get_face_normal ( actor.normal, vertex1, vertex2, vertex3 );		// multiply it by -1		actor.normal[0] *= -1;		actor.normal[1] *= -1;		actor.normal[2] *= -1;		cgGLSetParameter3f ( vertexPosition, vertex1[0], vertex1[1], vertex1[2] );		cgGLSetParameter3f ( vertexNormal, actor.normal[0], actor.normal[1], actor.normal[2] );		/*glBegin ( GL_LINES );		glVertex3f ( actor.vertex[actor.polygon[i].a ].x, actor.vertex[actor.polygon[i].a ].y, actor.vertex[actor.polygon[i].a ].z );		glVertex3f ( vertex1[0], vertex1[1], vertex1[2] );		glVertex3f ( actor.vertex[actor.polygon[i].b ].x, actor.vertex[actor.polygon[i].b ].y, actor.vertex[actor.polygon[i].b ].z );		glVertex3f ( vertex2[0], vertex2[1], vertex2[2] );		glVertex3f ( actor.vertex[actor.polygon[i].c ].x, actor.vertex[actor.polygon[i].c ].y, actor.vertex[actor.polygon[i].c ].z );		glVertex3f ( vertex3[0], vertex3[1], vertex3[2] );		glEnd ( );*/		// begin drawing polygon		glBegin ( GL_TRIANGLES );				// activate face normal		glNormal3f ( actor.normal[0], actor.normal[1], actor.normal[2] );				// draw first vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].a].u, actor.mapcoord[actor.polygon[i].a].v );		glVertex3f ( actor.vertex[actor.polygon[i].a ].x, actor.vertex[actor.polygon[i].a ].y, actor.vertex[actor.polygon[i].a ].z );				// draw second vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].b].u, actor.mapcoord[actor.polygon[i].b].v );		glVertex3f ( actor.vertex[actor.polygon[i].b].x, actor.vertex[actor.polygon[i].b].y, actor.vertex[actor.polygon[i].b].z );		// draw third vertex		glTexCoord2f ( actor.mapcoord[actor.polygon[i].c].u, actor.mapcoord[actor.polygon[i].c].v );		glVertex3f ( actor.vertex[actor.polygon[i].c].x, actor.vertex[actor.polygon[i].c].y, actor.vertex[actor.polygon[i].c].z );		// done drawing polygon		glEnd ( );    }	// pop the matrix	glPopMatrix ( );			// disable sphere mapping	glDisable ( GL_TEXTURE_GEN_S );	glDisable ( GL_TEXTURE_GEN_T );}

Do you need any more code parts?

##### Share on other sites
dimebolt    440
Quote:
 Original post by d h kSimple things first: I have no idea why that second image isn't showing up. I uploaded it to rapidshare just like I did with the first screen, and the first one works... Here is a new link that works!

Sorry, I should've been clearer. The link works, but the button 'free' in rapid share gives a missing/broken image symbol...

Quote:
 Original post by d h kHere is more code, that you requested:I initialize a light like this:

I notice two problems with the way you're using lighting. First, you don't use glMaterial to set the material properties, you use glColor. Although that is possible, it will only work if you call during your initialization:
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);glEnable(GL_COLOR_MATERIAL);

Furthermore, if you use white materials (glColor4f( 1.0f, 1.0f, 1.0f, 1.0f )) in combination with full out red ambient (ambient[] = { 1.0f, 0.0f, 0.0f, 1.0f };) everything will be fully red. I personally prefer debugging lighting with ambient set to black. Also, try setting specular or diffuse to black to find out which of the two causes the behaviour you're seeing. Check this page on my CG website to get more info on how the lighting calculation works.

Quote:
 Original post by d h kHere is my object::draw ( ) function, that draws models from 3ds files:

I'm not sure if 3ds files guarantee a consistent CW or CCW triangle definition. There is a simple test to check if the models you use are:
1. disable culling if necessary (glDisable(GL_CULL_FACE);)
const float red[] = {1,0,0,1};const float green[] = {0,1,0,1};glMaterialfv(GL_FRONT, GL_EMISSION, red);glMaterialfv(GL_BACK,  GL_EMISSION, green);

3. run your app and check the output:
If your models that are consistent CW or CCW will be either completely red OR green on the outside, where inconsistent objects will have green AND red triangles on the outside.

Tom

##### Share on other sites
d h k    439
First of all a big thank you to you, Tom, you are helping me out big time. :)

So, first I added the "glColorMaterial ( ... );" and the "glEnable ( GL_COLOR_MATERIAL );" calls to my init function. Then I changed my ambient light color to ( 0, 0, 0, 1 ) or black.

But that didn't change the problem. It looks actually way better because I set the ambient light color to black, but the problem is still there.

Then I followed your further advice and added the red/green coloring based on triangle orientation to my models... and... everything is fine. All models were absolutely red on the outside and absolutely green on the inside.

Any more ideas?

##### Share on other sites
dimebolt    440
Quote:
 Original post by d h kFirst of all a big thank you to you, Tom, you are helping me out big time. :)So, first I added the "glColorMaterial ( ... );" and the "glEnable ( GL_COLOR_MATERIAL );" calls to my init function. Then I changed my ambient light color to ( 0, 0, 0, 1 ) or black.But that didn't change the problem. It looks actually way better because I set the ambient light color to black, but the problem is still there.Then I followed your further advice and added the red/green coloring based on triangle orientation to my models... and... everything is fine. All models were absolutely red on the outside and absolutely green on the inside.Any more ideas?

I checked your normal calculation code and it seems fine (although I must say that I found the second for loop in get_face_normal() function a bit hard to read (I suggest writing a vector class that contains all vector math functionality (or you can have my math headers if you want, just PM me)).

Did you check setting diffuse/specular to black to find out which of the two causes the trouble? If it is specular you might want to set the shininess value to something decent (e.g. 12). Anyway, good luck with it, I'm signing off and will be away for the weekend. If this doesn't solve your problem you may want to make a new post on the OpenGL forum with the current status and see if you can get more help there, as I believe the problem is OpenGL and not the math.

Tom

##### Share on other sites
d h k    439
It's the diffuse color, that is causing the problem. Whenever I only use ambient and specular, the specularity doesn't seem to do anything. Wierd.

Thanks for your help and I'll probably start a new thread in the OpenGL area since you are right, this problem has become more of an OpenGL thing than a maths thing.