Archived

This topic is now archived and is closed to further replies.

Clouds3000

Calculating Normals, Dot product etc

Recommended Posts

Clouds3000    122
I, being a beginner am trying to get to grips with different techniques etc when making Games. So far, my little program loads from a .txt file and in this file you can specify the vertices for loading the quads, the texture to be used on the quads and the normal for each quad so I can enable lighting. BUT I its abit much to have to write in normals for each quad and I presume that you can create equations etc to calculate these from the vertices. I seen Nates normals tutorial but it said that you needed to know about dot product and cross product to understand it and I was wondering what these both are, what else they are used for and where can I find a tutorial on them so I can generate my own normals. Thanks guys

Share this post


Link to post
Share on other sites
SirKnight    316
Well if the mr-gamemaker.com site was still up like it used to be i would tell you to go there but since its down, only the message board is up i wont. The Dot and Cross Products are very important in the world of 3d graphics. The Cross product returns a vector, and this vector is perpendicular to the plane you created it from. Which is the normal. So to calculate the Cross Product, you need 2 vectors. Lets say you have a polygon with these three sets of points, (5,3,2) & (1,2,4) & (5,-3, 5). Ok to find the normal of this polygon, you need to create 2 vectors. Let P1 = (5,3,2), P2 = (1,2,4), P3 = (5,-3, 5). So your two vectors V1 and V2 are as follows. V1 = P2 - P1, V2 = P3 - P1. Now to find the normal you take the cross product of V1 and V2. So Let CP = the cross product between V1 and V2.

CP.x = (V1.y*V2.z)-(V1.z*V2.y)
CP.y = (V1.x*V2.z)-(V1.z*V2.x)
CP.z = (V1.x*V2.y)-(V1.y*V2.x)

Ok and if i did my math correctly you now have the normal of that polygon.

Now the Dot Product is a scalar (sp?) value. Its the angle between the two vectors you perform the dot product to. The angle is the cosine of the two vectors to be exact. The Dot product has many uses, one use is for determining the intensity of light on a vertex. Ok lets use the same 3 points from the cross product example in this example. The dot product is calculated as follows:

DP = (V1.x*V2.x)+(V1.y*V2.y)+(V1.z*V2.z)

Much simpler to calculate eh? Well i hope what all i did was helpfull to you. Notice i didnt use the actuall numbers in the calculation of the dot and cross product, so i hope you understand what i did.

-SirKnight

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Basically, it''s like this:

The dot product takes two vector arguments and returns a scalar(single number). This number is the length of the projection of one vector onto another. It''s not very important for your application. You calculate it by doing a component wise multiply, then adding.
So (1,2,3) Dot (4,5,6) = 1*4+2*5+3*6=32

Cross product takes two vectors and returns a vector perpendicular to both. This can be used to generate normals out of polygon information. How, you ask? Simply take your two input vectors as 2 of the polygon edges, then their cross product will be the normal, as it is perpendicular to both. It is important to observe winding - ie, if you define your points counterclockwise, take the cross product counter-clockwise as well. To compute the cross product, you have to make a determinant:
Cross of (x,y,z) with (a,b,c)
| i j k |
| x y z | = i(y*c-b*z) -j(x*c-a*z) +k(x*b-a*y)
| a b c |
or in vector form, (y*c-b*z,a*z-x*c,x*b-a*y)
Let''s say you get a triangle, with points P1,P2, and P3.
The right hand rule determines how the cross product works. The right hand rule works like this:
Take your right hand, and point the fingers in the direction of the first vector. Stick your thumb out perpendicular to your fingers(doesn''t matter where for now). Then align your hand so that the palm points in the direction of the second vector, so you should be able to curl your fingers in to sweep out the angle between the two vectors. The thumb points in the direction of the result of the cross product.

So for P1,P2, and P3(defined in that order, and assuming counter-clockwise winding), we want to take the cross product of (P2-P3) with (P2-P1) to find the normal for the triangle.

Additionally, you want to normalize your normal. Otherwise, lighting calculations get messed up. A normalized vector is simply one whose magnitude is 1, so sqrt(x^2+y^2+z^2) better equal 1.
To normalize a vector simply divide all components by the magnitude of the vector. You should only do this once for static geometry, as it is a very time consuming operation.

As for the dot product, you can use that to figure out which way a vector is pointing. Let''s say you have a viewing vector, (x,y,z). Now you can figure out if a polygon is facing toward or away from this viewing vector by taking the dot product. If the dot product of two vectors is positive, they are facing the same direction. If it is negative, they are going in opposite directions.
So say your polygon normal is (a,b,c).
If (a*x+b*y+c*z)<0, then the vectors are facing opposite directions and the polygon is facing towards the viewing vector.

-sjelkjd

Share this post


Link to post
Share on other sites
acraig    471
If you're intrested in learning a bit more about the dot and cross products then take a look for them in a couple of first year university physics books. They should have a couple of fancy diagrams that would really help. Plus it would give you some idea how it applies to other stuff like kinematics, which would be good to know anyways.

Or go to Google and search for "cross product tutorial". You should get a bunch of hits that can explain it.

-------
Andrew

Edited by - acraig on March 15, 2001 7:53:07 PM

Share this post


Link to post
Share on other sites
sjelkjd    171
just as a followup to SirKnight''s post, the Dot Product is only the angle between two vectors if they are normalized vectors. The actual formula is this:
u dot v = |u|*|v|*cos (theta)
The nice thing about the dot product is you only have to check signs, though. Cosine is positive for -pi/2<theta

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
I setup a function that calculated my normals, I used the technique from SirKnight and normalised them like sjelkjd said. Now all my polygons are drawn counter-clockwise starting with the top left vertex so I can enable GL_CULL. Now, after coding it into my program and testing it it seems to almost work. My walls are lit by the diffuse lighting but my floor and ceiling in my room are not, they are only lit when ambient lighting is enabled and they are calculated in the same way as the walls???
And if by chance I acidentally were to put the vertices of the ceiling and floor in the wrong order (not counter-clockwise) they wouldn''t show up when GL_CULL was enabled would they? Could this be the prob, its hard to draw a polygon count-clockwise when its not facing you.

Something that isn''t quite right is the fact that the polygons loaded from my file are quads, and I used SirKnight''s equation to calculate the normals which refers to triangles, I presumed that it would work because it uses the 1st 3 vertices of the quad to calculate the normal, and since if the first 3 vertices of the quad were to make a triangle it would be facing the same direction anyways, so it should still work should it not? And if it shouldn''t work, why are my walls lit by diffuse light?

Sorry if this all sounds like a load of rubbish, but cheers for the help.




Share this post


Link to post
Share on other sites
SirKnight    316
sjelkjd, thanks for posting your follow up. I am so used to making my vectors normals before doing the dot product that i forgot you calculate it a little differently if they are not normalized. Oops. Also, even though my example used a triangle, the calculation of the cross product is still the same if you are using a quad. Because as you probably know, a quad is made up of two triangles. And both of those triangles lay on the same plane, so the normal to the first triangle is the same as the second triangle. But if one vertex of one of the triangles has a diff z value than the rest, you would have to then take the cross product of both triangles. But if one z value was diff then i dont think it would be called a quad anymore. But i could be wrong.

-SirKnight

Edited by - SirKnight on March 17, 2001 2:04:37 PM

Share this post


Link to post
Share on other sites
Clouds3000    122
Ok, this is driving me crazy. Could some of you guys please tell me if what I am doing is correct.

I have stored all my vertex coorinates in an array from a data file.

I take the coordinates (the float numbers used when using glVertex3f(etc);
I take the x coordinate, the y and z of each of 3 vertices and enter them into the equation, then use the new variables in
glNormal(x,y,z);
As I said above it seems to work on the walls, but not the floor and ceiling. The wierd thing is that if I Disable GL_CULL_FACE and re-arrange the floor/ceiling coordinates they light up with diffuse lighting, but they won''t appear when GL_CULL_FACE is enabled. Anyone any ideas?

Thanks

Share this post


Link to post
Share on other sites
Nibbles    569
this is just a shot in the dark, but wouldn''t the normals for your floors simply be 0.0f, 1.0f, 0.0f and for your ceilings be 0.0f, -1.0f, 0.0f ??? assuming they are flat.

Scott

"If you try and don''t succeed, destroy all evidence that you tried."

Share this post


Link to post
Share on other sites
Clouds3000    122
Yea, but I can''t individually define normals to polygons because all the polygons (walls and floors/ceilings) are loaded from the data file in a ''for loop'', and everything in this loop has normals precalculated from the equations.

Share this post


Link to post
Share on other sites
Jallen    122
I believe GL_CULL_FACE culls polygons based one their winding. This means if you specified the triangle in clockwise order it is a back face, if you specify it in counter-clockwise order then it is a front face. Look into the glFrontFace function for more info.

Jason A.

---
I write code.
DelphiGL (http://delphigl.cfxweb.net)

Share this post


Link to post
Share on other sites
SirKnight    316
Well if when you have GL_CULL_FACE enabled they dont show, and disabled they do show, then is has to be the winding of the polygons are not correct.

-SirKnight

Share this post


Link to post
Share on other sites
Clouds3000    122
I know, this is what I mean. If the winding is correct (counter-clockwise in my case) the 4 walls have lighting but the roof and floor do not. But if I screw up the winding on the floor/roof and disable CULL_FACE then the floor/roof are affected by lighting. So the lighting only works on these 2 horizontal surfaces if the winding on them is screwed up.
ps-It is the vertex coordinates I''m meant to be plugging into the cross product ain''t it?
Thanks

Share this post


Link to post
Share on other sites
sjelkjd    171
quote:
Original post by Clouds3000

ps-It is the vertex coordinates I''m meant to be plugging into the cross product ain''t it?
Thanks


You should be crossing two edges of your polygon. The cross product gives you a perpendicular vector. So if you cross two vectors that are on the surface of your polygon, you get a perpendicular vector to the polygon.

Say you have a triangle with 3 vertices, v0,v1, and v2
Assuming you have a CCW winding, you want to cross
(v2-v1) X (v0-v1)


Share this post


Link to post
Share on other sites
Phantom Lord    122
OK...some corrections here......i read in some posts here that you get your normals by doing the following Vector calculations:
V1 X V2

You don''t get normals out of that calculation, you get a cross product.....a normal isn''t a cross product, a Normal is a Normalized cross product....that means a that the normal vector has the same direction as the cross product but it is of unit length (length = 1)
Now, with OGL you can use crossproducts as normals but you should call:
glEnable(GL_NORMALIZE);

If you forget to do this, then your lighting gets screwed.

To speed things up you can precalculate your normals by dividing X,Y and Z values of the Crossproduct Through the length of your cross product.

Now you have made you crossproduct unit length and thus you can call it a Normal.

Now that you have your normals you can render your scene without:
glEnable(GL_NORMALIZE);
and thus speeding up your render proces


OK...that was the teacher inside of me

Some code from a .3DS model loader i am coding, i use it to calculate the face Normals after i loaded the Model into memory,
it''s still unoptimized and untested, but i am pretty sure that it will work: ( hope this code doesn''t get too screwed in the post)

void CModel::CalculateNormals()
{
// first we calculate the face normals of all the faces of all the objects
Object *pObjectPointer = m_plstObjects;
Vector CrossProduct, VectorA, VectorB;
float CrossProductLength;

while (pObjectPointer != NULL)
{
for (int i = 0; i < pObjectPointer->shNumFaces; i++)
{
// first calculate the face edge Vectors which will be used to determine the CrossProduct
VectorA.x = pObjectPointer->parFaces.Corner3->x - pObjectPointer->parFaces[i].Corner2->x;
VectorA.y = pObjectPointer->parFaces[i].Corner3->y - pObjectPointer->parFaces[i].Corner2->y;
VectorA.y = pObjectPointer->parFaces[i].Corner3->z - pObjectPointer->parFaces[i].Corner2->z;

VectorA.y = pObjectPointer->parFaces[i].Corner1->x - pObjectPointer->parFaces[i].Corner2->x;
VectorA.y = pObjectPointer->parFaces[i].Corner1->y - pObjectPointer->parFaces[i].Corner2->y;
VectorA.y = pObjectPointer->parFaces[i].Corner1->z - pObjectPointer->parFaces[i].Corner2->z;

// calculate the crossproduct
CrossProduct.x = VectorA.y * VectorB.z - VectorA.z * VectorB.y;
CrossProduct.y = VectorA.z * VectorB.x - VectorA.x * VectorB.z;
CrossProduct.z = VectorA.x * VectorB.y - VectorA.y * VectorB.x;

//normalize the Crossproduct and save it to the correct FaceNormal
CrossProductLength = (float)sqrt((CrossProduct.x * CrossProduct.x) + (CrossProduct.y * CrossProduct.y) + (CrossProduct.z * CrossProduct.z));

pObjectPointer->parFaces[i].FaceNormal.x = CrossProduct.x / CrossProductLength;
pObjectPointer->parFaces[i].FaceNormal.y = CrossProduct.y / CrossProductLength;
pObjectPointer->parFaces[i].FaceNormal.z = CrossProduct.z / CrossProductLength;
}
pObjectPointer = pObjectPointer->pNextObject;
}
}

Share this post


Link to post
Share on other sites
Clouds3000    122
Thanks for all your help guys, this is what I did.
I threw away my little level made of quads, cause no matter what I did, some of the faces wouldn't light up. I changed my program alittle bit to load in triangles instead of quads and used the level data from Lesson10 so I wouldn't have to takes ages typing out the data. And.........IT WORKED. As far as I can see its working, I presume there must have been some crap-up using the quads when calculating the Cross-product.
One more thing though, I move around my level using Lesson10 code, so I rotate the polygons around me instead of moving the camera. But I want a main light in the room (which works) and a light that follows me around but I wasn't sure how to do this cause if I specified the coordinates of 'my' light to be (0,0,0) ie-where I am, it will get rotated like all the others points. The light that follows me is yellow, though even when I turn it on the room is white (mixture of the original blue light and the yellow light ??????), but if I stick a glLoadIdentity in front of the light position that follows me like so.....
glLoadIdentity();
(glLightfv(GL_LIGHT2, GL_POSITION,LightPosition2);
then if I duck down the ceiling goes a darker yellow color and the floor dark blue. I understand the floor goin' blue cause if I am too close to it I am 'on top' of its normal but the ceiling going yellow? I don't understand.
Sorry for being such a slow learner with this dam lighting thing
Cheers again.

Edited by - Clouds3000 on March 22, 2001 5:25:32 PM

Share this post


Link to post
Share on other sites