(Return to list of 3D Math articles)
Welcome to the second part of 3D Math Basics articles. Here I will try to explain two very important vector operations - the cross and dot product.
Cross product is very important in 3D graphics. With it, we can calculate the normal of polygon. For those, who doesn't know what is the normal, it is a directional vector, which is perpendicular to the plane (there is a right angle between normal and plane), in which polygon lies, so it is also perpendicular to this polygon. It is used for thousands of things, for example lighting calculations or back-face culling. And for those, who doesn't understand what is the plane, it's just a flat area in some 3D space. It can be defined by 3 points, which are not collinear - they don't lie on the same line. Look at the picture:
So this is the plane (with yellow color):
It goes on forever, so actually this is just part of a plane. I hope you got the idea of what is the plane. When we have 3 (or more) points there, they form a triangle (or polygon). But in order to set back-face culling and achieve nice lighting effects, we need to define a normal vector. How to do it? We need to take cross product. It's always taken from two directional vectors and resulting vector will be normal. This normal is perpendicular both to first and second directional vector. Look at this:
We've got two directions - green is P2 - P1 and red is P3 - P2. Now, here is how is the cross product calculated:
CVector3 vCross; // Here we will store it // Get the X value
vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y)); // X value
vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z)); // Y value
vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x)); // Z value
Green vector is (0, 0, 1) and red is (1, 0, 0). If we put these vectors as parameters, the resulting vector is (0, 1, 0) - vector pointing straight up! And it is perpendicular to both vectors: Look:
I hope you now got it. But there is one problem. What if the resulting vector was (0, -1, 0)? It would be perpendicular too. How to know which direction will be vector pointing? Well, I heard something about
right-hand rule, but I didn't get the idea of it. But I found out that it depends on whether vectors are in clockwise or anticlockwise order. The best way to explain it is an example:
Edit on 21.01.2012: I was a n00b back then, and I'm not much of a pro right now, but I should be less n00b . Right-hand rule is very easy and I didn't even try to understand it back then. Take your right hand, have thumb, index finger and middle finger be perpendicular, your thumb finger points as vVector1, your index finger as vVector2, and middle finger shows direction of resulting vector from cross product. Take a look at wikipedia for a picture http://en.wikipedia.org/wiki/Right-hand_rule.
It's a cube. Now, we want to calculate its normals. For lighting effects and back-face culling. We will take as example the red side (front):
We want its normal to be pointing to the front (so the resulting vector should have direction (0, 0, 1) ). Look at this code:
fourNormals = vecCross(P2 - P1, P3 - P2);
fourNormals = vecCross(P3 - P2, P4 - P3);
fourNormals = vecCross(P4 - P3, P1 - P4);
fourNormals = vecCross(P1 - P4, P2 - P1);
for(int i = 0; i < 4; i++)vecNormalize(fourNormals[i]);
Now all of the four normals are the same and are pointing to the front (to us). Of course, you don't need to calculate all 4 (or more, if polygon has more vertices), you just need to calculate one normal.
You cannot forget to normalize it, because its length probably won't be 1. Normal's length must always be 1. As you can see, it's pointing where we wanted it to point. I used counter-clockwise
order. Look (green vector is normal):
Edit on 21.01.2012: Directions put here correspond with right hand rule, ignore clockwise and counter-clockwise stuff .
Another important operation is dot product. It can be used to find angle between two directional vectors. It can be used when you want to find angle to rotate some object to face another. The dot product doesn't return a vector, it returns only a number (scalar). The dot product is calculated this way:
return (vVector1.x * vVector2.x) +
(vVector1.y * vVector2.y) +
(vVector1.z * vVector2.z);
If we have only 2 dimensions, we would remove z from this formula (the dot product calculation is same for any number of dimensions, but this isn't much important here). So this is how we calculate it.
In any book that deals with math, you will find something like this about dot product:
A.B = |A|*|B|*cos theta
It means that dot product of vector A and B equals to length of A * length of B * cosine of angle between them. We want to find the angle. From that:
cos theta = (A.B) / (|A|*|B|)
So now we have the cosine of angle. To get actual angle, we need to use arc cosine. Following code does it all:
float fDotProduct = vecDot(vVector1, vVector2);
float fVectorsMagnitude = vecMagnitude(vVector1) * vecMagnitude(vVector2) ;
if(fVectorsMagnitude == 0.0)return 0.0; // Avoid division by zero
double angle = acos(fDotProduct / fVectorsMagnitude);
// The angle is in radians, so convert it to degrees
return angle * 180 / PI;
It returns angle from 0 to PI (0 to 180).
If you want to see usage of dot and cross products, check the codes of Ruined city of Verion (OpenGL Misc).
Now that's all I know about this. And I think it's enough. Yet. Maybe later it won't be enough and I will have to dive deeper into cross and dot products. I hope you learned something from this article. If you've got any questions about this article, put them either into Message Board or e-mail them to firstname.lastname@example.org. And try to play around with these cross and dot products in order to fully understand them.