• Advertisement
Sign in to follow this  

Computing Face Normals (Updated with SCREENS)

This topic is 4579 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

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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 ( );
}

Share this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 light
float 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?

Share this post


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

  • Advertisement