Sign in to follow this  
d h k

Computing Face Normals (Updated with SCREENS)

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][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 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[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 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[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 this post


Link to post
Share on other sites
Quote:
Original post by d h k
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!

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 k
Here 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 k
Here 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);)
2. in your rendering function replace your glColor4f(1,1,1,1) call with:

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 this post


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


Link to post
Share on other sites
Quote:
Original post by d h k
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?


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 this post


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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this