Computing Face Normals (Updated with SCREENS)

Started by
12 comments, last by d h k 18 years, 6 months ago
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] = point[2-a] - point[0];      
		}
	}

	// 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.a ].x;
		vertex1[1] = p_object->vertex[p_object->polygon.a ].y;
		vertex1[2] = p_object->vertex[p_object->polygon.a ].z;
		vertex2[0] = p_object->vertex[p_object->polygon.b ].x;
		vertex2[1] = p_object->vertex[p_object->polygon.b ].y;
		vertex2[2] = p_object->vertex[p_object->polygon.b ].z;
		vertex3[0] = p_object->vertex[p_object->polygon.c ].x;
		vertex3[1] = p_object->vertex[p_object->polygon.c ].y;
		vertex3[2] = p_object->vertex[p_object->polygon.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]
Advertisement
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.
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.a ].x;		vertex1[1] = actor.vertex[actor.polygon.a ].y;		vertex1[2] = actor.vertex[actor.polygon.a ].z;		vertex2[0] = actor.vertex[actor.polygon.b ].x;		vertex2[1] = actor.vertex[actor.polygon.b ].y;		vertex2[2] = actor.vertex[actor.polygon.b ].z;		vertex3[0] = actor.vertex[actor.polygon.c ].x;		vertex3[1] = actor.vertex[actor.polygon.c ].y;		vertex3[2] = actor.vertex[actor.polygon.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.a].u, actor.mapcoord[actor.polygon.a].v );		glVertex3f ( actor.vertex[actor.polygon.a ].x, actor.vertex[actor.polygon.a ].y, actor.vertex[actor.polygon.a ].z );				// draw second vertex		glTexCoord2f ( actor.mapcoord[actor.polygon.b].u, actor.mapcoord[actor.polygon.b].v );		glVertex3f ( actor.vertex[actor.polygon.b].x, actor.vertex[actor.polygon.b].y, actor.vertex[actor.polygon.b].z );		// draw third vertex		glTexCoord2f ( actor.mapcoord[actor.polygon.c].u, actor.mapcoord[actor.polygon.c].v );		glVertex3f ( actor.vertex[actor.polygon.c].x, actor.vertex[actor.polygon.c].y, actor.vertex[actor.polygon.c].z );		// done drawing polygon		glEnd ( );    }
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.
Are your light within viewing volume?
What kind of attenuation model are you using?
Are you try
glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_TRUE);
yet?
Thanks for your suggestions, sakana321.

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!
Quote:Original post by d h k
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.


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.

And finally, I could not download the second image you posted...

Tom
Thanks for your help, dimebolt.

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.a ].x;		vertex1[1] = actor.vertex[actor.polygon.a ].y;		vertex1[2] = actor.vertex[actor.polygon.a ].z;		vertex2[0] = actor.vertex[actor.polygon.b ].x;		vertex2[1] = actor.vertex[actor.polygon.b ].y;		vertex2[2] = actor.vertex[actor.polygon.b ].z;		vertex3[0] = actor.vertex[actor.polygon.c ].x;		vertex3[1] = actor.vertex[actor.polygon.c ].y;		vertex3[2] = actor.vertex[actor.polygon.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.a ].x, actor.vertex[actor.polygon.a ].y, actor.vertex[actor.polygon.a ].z );		glVertex3f ( vertex1[0], vertex1[1], vertex1[2] );		glVertex3f ( actor.vertex[actor.polygon.b ].x, actor.vertex[actor.polygon.b ].y, actor.vertex[actor.polygon.b ].z );		glVertex3f ( vertex2[0], vertex2[1], vertex2[2] );		glVertex3f ( actor.vertex[actor.polygon.c ].x, actor.vertex[actor.polygon.c ].y, actor.vertex[actor.polygon.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.a].u, actor.mapcoord[actor.polygon.a].v );		glVertex3f ( actor.vertex[actor.polygon.a ].x, actor.vertex[actor.polygon.a ].y, actor.vertex[actor.polygon.a ].z );				// draw second vertex		glTexCoord2f ( actor.mapcoord[actor.polygon.b].u, actor.mapcoord[actor.polygon.b].v );		glVertex3f ( actor.vertex[actor.polygon.b].x, actor.vertex[actor.polygon.b].y, actor.vertex[actor.polygon.b].z );		// draw third vertex		glTexCoord2f ( actor.mapcoord[actor.polygon.c].u, actor.mapcoord[actor.polygon.c].v );		glVertex3f ( actor.vertex[actor.polygon.c].x, actor.vertex[actor.polygon.c].y, actor.vertex[actor.polygon.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?

This topic is closed to new replies.

Advertisement