The World of 3-D Graphics
Part 2: Vectors and Planes

by David Bull

Prelude to a Greater Understanding

I got a lot of feedback from the first article, much of it complaining about its simplicity. Well, soon you’ll be screaming at me to go back to the simple stuff. This time, I’ll be discussing basic vector math and we will also be taking a look at planes. The following information is the kind of stuff that you need to understand if you wish to succeed in the world of 3-D graphics. So listen up well, and soon you’ll be doing all kinds of neat things.

What is a Vector?

When I discussed points I mentioned that a vector consisted of similar properties to a point. Well here is the point structure again.

struct Point
{
  float x;
  float y;
  float z;
};

And introducing our vector structure…

struct Vector
{
  float x;
  float y;
  float z;
};

"Holy Jamoly", I can hear you say, "They look exactly the same". Well, yes that is true, but where they differ is in what they represent mathematically. Remember a point is used to reference a particular position in our coordinate system. Well rather than position, a vector represents two new concepts: magnitude and direction.

When we speak of magnitude we speak of length, and direction obviously means which way the vector is pointing.


Figure 1. A coordinate system with a vector

Imagine that each axis on this coordinate system is 5.0 units in length. You can see our vector < 2.0x, 4.0y, 0.0z >, which is pointing 4.0 units up the y axis and 2.0 units across the x axis. How long is it? Let’s find out.

How long is your Vector?

In many vector operations which are performed, prior knowledge of the length of the vector is required. To find the length of a vector you take the square root of the sum of squares of each vector component as shown below.

|V| = sqrt( x2 + y2 + z2 )

Note that our vector V is denoted using the | | symbol. This means that we are referring to the vector's length. Using the above formula our code may look something like this:

inline float Length( Vector &v )
{
  return( sqrtf( (v.x*v.x) + (v.y*v.y) + (v.z*v.z) ) )
}

Using the vector in Figure 1, we will find the length. The vector length equation is perfomed on our vector < 2.0x , 4.0y, 0.0z >, and we find the length equal to 4.47214.


Figure 2. Our vector's length

Normalized Vector

Also known as the unit vector, a normalized vector's length is equal to exactly 1. To find the normal vector we must calculate the length, and then divide each component of the vector by that length.

Normal = V/ |V|

V = ( 3.0x, 6.0y, 2.0z )

|V| = 7.0

V.x /= |V|
V.y /= |V|
V.z /= |V|

V.x = 0.428
V.y = 0.857
V.z = 0.285

|V| = 1.0

So we have our vector V with its values described above. We calculate the magnitude of the vector and we find that its length is 7.0. Following that we divide each component by the magnitude. If we calculate the length of the vector again we now find that it is equal to 1.0, the very value which makes it a normal vector. Remember that although the vector's length may now be equal to 1.0 its direction is still the same. Here is a way we can do it in code.

inline Vector Normalize( Vector &v )
{
  float temp = 1/Length( v );
  v.x *= temp;
  v.y *= temp;
  v.z *= temp;
  return( v );
}

Notice how we cached the division. That’s a nice optimization because multiplication is faster than division.

Vector Operations

We can perform operations on vectors like we would on our everyday numbers such as addition, subtraction, and multiplication (though multiplication has two different interpretations).

Addition


Figure 3. Vector Addition

inline Vector AddVect( Vector &v1, Vector &v2 )
{
  Vector v;
  v.x = v1.x + v2.x;
  v.y = v1.y + v2.y;
  v.z = v1.z + v2.z;
  return( v );
}

Subtraction


Figure 4. Vector Subtraction

inline Vector SubVect( Vector &v1, Vector &v2 )
{
  Vector v;
  v.x = v1.x - v2.x;
  v.y = v1.y - v2.y;
  v.z = v1.z - v2.z;
  return( v );
}

Multiplication with a Scalar Number

To increase the length of a vector we can multiply with a scalar number.

inline Vector ScaleIncrease( Vector &v, float &s )
{
  v.x *= s;
  v.y *= s;
  v.z *= s;
  return( v );
}

Division with a Scalar Number

To decrease the length of a vector we can divide with a scalar number.

inline Vector ScaleDecrease( Vector &v, float &s )
{
  v.x /= s;
  v.y /= s;
  v.z /= s;
  return( v );
}

Dot Product

The Dot Product is a vector operation which will return a scalar value (single number), which for unit vectors is equal to the cosine of the angle between the two input vectors (for non-unit vectors, it is equal to the length of each multiplied by the cosine, as shown in the equation below). We can represent the Dot Product equation with the ● symbol.

U ● V = |U| * |V| * cos(q)

alternatively

( x1, y1, z1 ) ● ( x2, y2, z2 ) = x1y1 + x2y2 + z1z2


Figure 5. Dot Product

In Figure 5, there are two vectors pointing in different directions. Where those vectors meet you can find the dot product. Before we do anything else let’s take a look at the dot product code.

inline float DotProduct( Vector &v1, Vector &v2 )
{
  return( (v1.x*v2.x) + (v1.y*v2.y) + (v1.z*v2.z) );
}

This seems to be a complex task, but it's actually quite simple in code. All we did was multiply each vector component together and sum the products. Our returned value is a scalar value - not a vector - and this is our dot product.

It won’t take long in your exploration of 3-D graphics before you find yourself using the dot product frequently. Among other things, it is useful in backface culling, lighting and collision detection.

Cross Product

Another useful vector operation is the cross product. Unlike the dot product which returns a scalar value the cross product actually returns a vector. The vector which is returned is perpendicular to the two input vectors.

inline Vector CrossProduct( Vector &v1, Vector &v2 )
{
  Vector v;
  v.x = ( v1.y * v2.z ) – ( v1.z * v2.y );
  v.y = ( v1.z * v2.x ) – ( v1.x * v2.z );
  v.z = ( v1.x * v2.y ) – ( v1.y * v2.x );
  return( v );
}

In 3D graphics, the most common use of the cross product is to calculate a polygon normal. Calculating the polygon normal can be great for backface culling and lighting. We do this by doing a cross product on two vectors that lie on the polygon's plane (more on planes in a sec).


Figure 6. Vector calculated using the cross product

What is a Plane?

A plane is like a huge piece of paper. Each triangle making up our models lies in its own plane.

ax + by + cz + d = 0

The above equation describes our plane. The < a, b, c > triplet describes the normal of our plane. The normal is the vector which is perpendicular to all of the points which lie on the plane. We learned how to calculate the normal when we were learning about the cross product. The d component is a scalar value which represents the distance from the plane to the origin < 0x, 0y, 0z >. The triplet < x,y,z > is the point which solves the equation. You will understand this in a moment.


Figure 7. Example of plane

In this diagram, the normal points away from the origin, so the distance is negative. If the normal pointed toward the origin, the distance would be a positive value. Obviously if the plane goes through the origin then the distance would be equal to zero.

Constructing a Plane

// global variables
Vector norm;
float  distance;

void ConstructPlane( Vector &p0, Vector &p1, Vector &p2 )
{
  norm = CrossProduct( SubVect(p2 ,p1), SubVect( p0,p1) ); 
  norm = Normalize(norm);
  distance = -DotProduct( norm, p1 );
}

Not too complicated; all we are doing here is performing a cross product on the two vectors which are made up of the three points. We do this to find the normal and then we make it unit length. To find the distance we calculate the negative dot product of the normal with the point of your choice.

Defining the location of a point in relation to a plane

Defining the location of a point in relation to a plane is one of the most common operations we can do with planes. There are three possible cases when perfoming this operation. They are: front of the plane, back of the plane, and coplanar with the plane. So, how do we tell the difference between the front of the plane and the back of the plane? The front is defined as the side which the normal sticks out of, so obviously the back of the plane is the opposite side.


Figure 8. Point Classification

V ● N + D

Looking at Figure 8, we can see that vector V is at the back of the plane. How did we come to this conclusion? We find the dot product of our vector V with the normal and add the distance. If we get a negative value returned then the point is behind the plane and if we get a positive value, the point is in front of the plane. If the point lies on the plane then it is coplanar.

The returned value is the distance from the plane. Remember that if that value is equal to zero than we are indeed coplanar with the plane.

Conclusion

If you have made it this far then well done. Compared to what we have ahead of us this stuff is relatively simple but those who haven’t studied vector math before may find it a little confronting. Don’t worry to much, once you start using this stuff more everything will fall into its place. Next time we dwelve into the mysteries of the matrix. Well I guess this is goodbye, I’ll see you next time.

Love me? Hate me? Questions? Suggestions? How about you e-mail me here.

Discuss this article in the forums


Date this article was posted to GameDev.net: 7/26/2001
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
General

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!