Using Vectors and Points

[ LiB ]

Using Vectors and Points

A single dimensional value is called a scalar . A scalar is any real number that can be arithmetically used, for example 1, 2, 37, 50.5, 34.9, 100, or 1024. Scalars are used in almost everything from accounting to counting (smile), but are limited because they are only one dimensional in definition. Scalars define magnitude but in the definition of 2D and 3D space, scalars just cannot be used; therefore, points and vectors came to be as the solution.

Vectors and points are simply two- or three-component scalars that have a specific meaning. Vectors and points are covered together because they are like distant cousins. Points just define a location in space as opposed to vectors, which define direction and magnitude. Points can be seen as locations in space that define the area of an object. For example, points are commonly used as the center location of spheres and the vertices of triangles or polygons. Points come in the form of two (x and y) or three (x, y, and z) components , as do vectors.

Vectors are a bit more complex because they use the same (x, y, z) components in a different form. Instead of using them as locations, they use them for direction and magnitude. The direction can be visualized as the movement from one point to another in 2D or 3D space. The magnitude of a vector is the distance from the world origin (0, 0, 0) to the point's current location. This value can be easily found using the Pythagorean theorem. Vectors are important to 2D and 3D graphics because they represent the arrows of direction and length in a simple form.

Let's begin with the construction of the Vector class that will also be used for points. This chapter discusses each method in detail to give you a broader understanding of each area.

The Vector Class

The class data will be composed of the x, y, z components, arithmetic operators, and axis-aligned vector constants. You will also have a variety of member functions that will be defined to follow a set of rules for manipulating vec-tors. It is very important to define this class thoroughly because it's the building block for the 3D graphics application.

 struct cVector3 {    union    {       struct       {              float x,y,z; // Three real components of the vector          }; float v[3]; // Array access useful in loops }; // Default constructor cVector3() { } // Custom three-member input constructor cVector3( float X, float Y, float Z ) :    x(X), y(Y), z(Z) { } // Return the magnitude of a point float Mag() const; // Normalizes a vector (makes its length one) void ormalize(); // Invert the vector void Invert();    // Constant vectors   static const cVector3  vZero;   // <0,0,0>   // Custom operators   cVector3& operator += ( const cVector3& in );   cVector3& operator -= ( const cVector3& in );   cVector3& operator *= ( const float& in );   cVector3& operator /= ( const float& in );   cVector3  operator  - (); // this is plain old negation }; 

Constant Vectors

The cVector3 class contains only one constant. The constant is a vector that is set to (0,0,0) for the internal components. By adding a vector constant, you can simplify the process of assigning (0,0,0) to the internal components at any time in the application.=

 // for constant zero vector const cVector3 cVector3::vZero   ( 0.f, 0.f, 0.f ); 

Using Vector Operators

You are going to use the mathematical arithmetic operators for the cVector3 class. You can add, subtract, multiply, and divide vectors just as you can scalars. Using C++ makes the code more readable and clearer because it takes out redundant structure- related statements and makes them structure-indepen-dent. You'll learn to expand the data structure as much as you can to accommodate any type of situation you might encounter using vectors and points.

NOTE

NOTE

Using the arithmetic operators in the vector class makes vector mathematics much simpler.The different vector methods are inde pendent to the vector class and are utilized via the operators.This helps to make code much easier to read and understand.

Vector Addition

You can add vectors the same way that you add scalars (8+2=10). The only distinction is that the extra component(s) must be taken into account for addition. A typical example is V1<3,2,0> + V2<0,2,0> = V3<3,4,0> . Figure 6.1 shows an example of vector addition.

Figure 6.1. Vector addi tion lets you add two vec- tors together by using the + operator;for example V1<3,2,0> + V2<0,2,0> = V3<3,4,0>.

graphic/06fig01.gif


The process of adding vectors can be graphical or numerical. From a graphical point of view, you take the first vector placed with the starting point at the origin and then "add" the other vector to it simply by placing the second vector's starting point at the end or tip of the first vector. The resulting vec-tor is constructed by drawing a third vector from the origin to the tip of the second vector. On the other hand, to add vectors numerically , you simply add the x components, they components, and the z components, respectively. Let's define the pseudo code. This is all possible by utilizing the + operator.

 inline cVector3& cVector3::operator += ( const cVector3& in ) {    x += in.x;    y += in.y;    z += in.z;    return *this; // This is so we can chain operations together } inline const cVector3 operator+(cVector3 const &a, Vector3  onst &b) {    return cVector3    (       a.x+b.x,       a.y+b.y,     a.z+b.z   ); }; 

Vector Subtraction

The process of subtracting vectors is the opposite of adding vectors. From a graphical point of view, you take the first vector placed with the starting point at the origin and then "subtract" the other vector simply by placing the second vector's starting point at the end or tip of the first vector. The resulting vector is constructed by drawing a third vector from the origin to the tip of the second vector. To subtract vectors numerically, you simply subtract the x components, the y components, and the z components, respectively.

Vector subtraction is subtracting one vector from another vector or from a scalar. A typical example is V1<3,5,0> V2< 1,2,0> = V3<4,3,0> . See Figure 6.2 for an example of vector subtraction.

Figure 6.2. Vector sub traction lets you subtract two vectors by using the operator.For example, V1<3,5,0> V2<1,2,0> = V3<4,3,0>.

graphic/06fig02.gif


Vector subtraction is the opposite of vector addition. The operator is used for vector subtraction. The procedure for vector subtraction is subtracting the x, y, and z components between the two vectors. Vector subtraction can be used to scale and translate objects. Let's define the pseudo code:

 inline cVector3& cVector3::operator -= ( const cVector3& in ) {   x -= in.x;   y -= in.y;   z -= in.z;     return *this; // This is so we an  hain operations together } inline const cVector3 operator-(cVector3 const &a, cVector3 const &b) {    return cVector3    (       a.x-b.x,       a.y-b.y,       a.z-b.z    ); }; 

Vector Scaling

Vector scaling is just like scalar multiplication. The only difference is that the scalar must be multiplied by each (x, y, and z) component of the vector. Scaling a vector involves multiplying each x, y, and z component of one vec-tor by a scalar. In a graphical example, the first scaled vector yields a new vector that is either less than or more than the first vector's tip. Vector scal-ing is typically used to make 3D objects smaller or larger. For example, multiplying the scalar by the vector is illustrated as V1<2,2,0> * 2 = V2<4,4,0> . See Figure 6.3 for an example of vector scaling. The new point location is determined after scaling the vector, whereby each component of the vector is multiplied by the scalar. The general idea of vector scaling is that it stretches a vector by a scaling factor. This is all possible using the * operator.

Figure 6.3. Vector scal- ing stretches a vector by a scalar;for example, V1<2,2,0> * 2 = V2<4,4,0>.

graphic/06fig03.gif


 inline cVector3& cVector3::operator *= ( const float& in ) {    x *= in;    y *= in;    z *= in;    return *this; // This is so we can chain operations together } inline const cVector3 operator*(cVector3 const &a, float const &b) {    return cVector3    (       a.x*b,       a.y*b,       a.z*b    ); }; inline const cVector3 operator*(float const &a, cVector3 const &b)     return cVector3     (        a*b.x,        a*b.y,        a*b.z     ); }; 

Vector Division

Vector division can be used to scale a big object into a smaller one or vice versa. The scaling of a vector is represented as V1<6,3,0> / 3 = V2<2,1,0> . See Figure 6.4 for an example of vector division. Vector division is the opposite of vector scaling. It shrinks a vector by a scaling factor. This is all possible using the / operator.

Figure 6.4. Vector division shrinks a vector by a scalar factor;for example, V1<6,3,0> / 3 = V2<2,1,0>.

graphic/06fig04.gif


 inline cVector3& cVector3::operator /= ( const float& in ) {    float inv = 1 / in;       // do one (expensive) divide and three (cheap) multiplies    x *= inv;    y *= inv;    z *= inv;    return *this; // This is so we can chain operations together } inline const cVector3 operator/(cVector3 const &a, float const &b) {    float inv = 1 / b;    return cVector3    (       a.x*inv,       a.y*inv,       a.z*inv    ); }; 

Vector Inversion

Vector inversion is the process of flipping the signed axes of the internal (x, y, z) components. The invert (or inverse) of a vector is illustrated as V1<4, 0, 0> * 1 = V2<4, 0, 0> . The purpose of invert is to flip a vector in the opposite direction. For example, if a vector were traveling in one direction, it would then be traveling in the opposite direction after being inverted. Think of it as the two lanes on the highwayyou're either going one direction or the other. Vector invert flips a vector by multiplying it by 1, as shown in Figure 6.5.

Figure 6.5. Vector invert flips a vector by multiplying it with 1;for example, V1<4,0,0> * 1 = V2<-4,0,0>.

graphic/06fig05.gif


 inline cVector3  cVector3::operator - () {    return cVector3(       -x,       -y,       -z    ); } inline void cVector3::Invert() {    x = -x;    y = -y;    z = -z; } 

Vector Assignments

You are going to write a function that assigns the internal x, y, and z components to three passed parameters. The three passed parameters are X, Y, and Z.

 inline void cVector3::Assign( float X, float Y, float Z ) {    x=X;    y=Y;    z=Z; } 

Vector Equality

Vector equality is the same process as testing scalars. It would be nice to test all components ( x,y,z = x2,y2,z2 ) by each other, but again you encounter floating-point number imprecision issues. This requires an epsilon that is equal to 0.001 ; think of the epsilon as a very tiny number. You can use it to determine whether two floating-point numbers are close enough to each other to be equal. The method passes two vectors as parameters. The internal components are subtracted from each other and compared to the epsilon. If the distance between the two vectors is smaller than the epsilon for the (x, y, and z) components, the method returns true.

 inline bool operator==(cVector3 const &a, cVector3 const &b) {    if(fabs(a.x-b.x)<EPSILON)    {       if(fabs(a.y-b.y)<EPSILON)       {          if(fabs(a.z-b.z)<EPSILON)          {             return true;          }       }    }    return false; }; 

NOTE

NOTE

The advantage to using the equality operator == for a comparison between two vectors is that it's simi lar to using the same operator for doing a comparison in C with char- acters,integers,and real numbers.

Vector Magnitude

The magnitude of a vector describes its length. In many cases, you'll want vectors to have unit length of 1.0 because it makes the math easier to work out. The magnitude of a vector is the distance from the origin to the tip of the vector. To find the magnitude of a vector, you must go back to the old days of geometry. The answer lies by using the Pythagorean theorem (A_ + B_ = C_). Normally, all points are offsets that begin at the world origin, which is (0,0,0). See Figure 6.6 for an example. Let's try to find the magnitude of 2D vector (3,2).

Figure 6.6. The mag nitude of a vector is its length.

graphic/06fig06.gif


The process of finding the magnitude of a vector is as follows :

  1. You must first determine how far each component is away from the origin (0,0) on each axis.

    You know that the x component of the vector is positive 3 units away from 0 and can be visualized as the adjacent side of the triangle.

    You know that the y component is the hypotenuse of the triangle and is 2 units up.

  2. To solve for the adjacent or the missing side, you simply use the Pythagorean theorem. For example:

     (3*3 = 9 + 2*2 = 13) = square root of (13) = 3.6 

    The answer is 3.6 for the magnitude, which means that the vector is 3.6 units away from the ray's origin.

Many programmers normalize the direction vector to 1.0 and use the magnitude to find the offset position for a point that is on the vector's path . You'll learn about this soon when constructing the ray class. The demonstration uses a 2D vector, but the process is the same for 3D vectorsyou just have to add the extra z component.

 inline float cVector3::Mag() const {    return (float)sqrt( (*this) * (*this) ); } 

NOTE

NOTE

The Pythagorean theorem is used to find the magnitude of a vector.

The Dot Product

The dot product is one of the most important procedures in vector mathematics. You'll likely use it more than any other vector function. It is used extensively because it returns very important information on the relative angle between two vectors. One of the dot product's most important uses is to compute the angle between two vectors. The equation of the dot product of two vectors U and V is as follows:

 (U . V) = (ux*vx, uy*vy, uz*vz) = cos (theta) * U * V 

Now of course U dot V is just a simple multiplication of the vectors' components but the symbolic meaning goes farther than what you see.

So rearranging terms a little, you can solve for the angle theta:

 cos (theta) = (ux*vx, uy*vy, uz*vz)                       U * V 

This means that (U . V ) can give a gross relationship between these two vec-tors. The cosine function has a range of 1 to 1. If the angle is more than 0, the two vectors face each other. If the angle is less than 0, they are facing away from each other. Figure 6.7 shows an example of the dot product.

Figure 6.7. The dot product finds the angle between the two vec- tors U and V.

graphic/06fig07.gif


To find the angle, you simply multiply vector U * V . Again, the formula is as follows: U dot V = U * V = cosine (angle). The result is in unit space because in trigonometry, the ratio of division between two sides of a triangle is all that really matters. Unit components can be scaled up to any large number, but are the same in ratio. So it doesn't really matter if you're dividing ( SideA [8] / SideB [4] = 2.0 ) or ( SideA [1.0] / SideB [0.5] = 2.0 ) , you'll get the same ratio. Try to imagine you are working with very tiny triangles with sides of lengths in millimeters that produce results of (0.5000 = sin(30 °), 0.5000 = cos(60 °)) in the division between two triangle sides at some arbitrary angle. Figure 6.8 illustrates the angular meaning between two vectors.

Figure 6.8. The angles between vectors using the dot product.

graphic/06fig08.gif


The meaning of the returned result (shown in Figure 6.8) is as follows:

  • If ( A dot B ) is > 0 , the angle between A and B is acute and is less than 90 degrees.

  • If ( A dot B ) is < 0 , the angle between A and B is obtuse and is more than 90 degrees .

  • If ( A dot B ) is = 0 , the vectors are perpendicular and the angle is 90 degrees .

The dot product is used for hidden surface removal, lighting calculations, and many more operations. It's used in ray tracing to calculate the direct lighting for diffuse surfaces. Let's define it as the * operator for the vector class.

 inline const float operator*(cVector3 const &a, cVector3 const &b) {     return a.x*b.x + a.y*b.y + a.z*b.z; } 

NOTE

NOTE

The dot product is extensively used in 3D games for hidden surface removal.

The Cross Product

Many times in the development of 3D graphics algorithms, you'll need to find the normal of two vectors. The normal is a vector that is perpendicular to the two vectors in question, thus creating a mutually perpendicular (orthogonal) system. Triangles are defined by three points in 3D space and polygons are just an extension of triangles with n points. Remember that you need at least three points to define the orientation of a triangle and polygon. The cross product is used to find a vector that is facing away from the triangle or polygon face. This value is normally a length of one, which brings up the next topic. See Figure 6.9 for an example of the cross product.

Figure 6.9. The cross product is a perpendicular vector between three points on a surface.

graphic/06fig09.gif


The cross product is used for the following:

  • It can be used to compute the normal for a flat surface (polygon, triangle).

  • It can be used to compute the direction a polygon or triangle is facing.

  • It computes the angle relationship between triangles and polygons. This is conducted by using the simple dot product method (angle between two vectors). See Figure 6.10 for an example of the face-to-face relationship. This is normally used to determine whether a series of polygon/triangles are facing an arbitrary polygon/triangle.

    Figure 6.10. The face-to-face relation ship between flat surfaces (such as polygons and triangles).

    graphic/06fig10.gif


  • It generates a plane for a triangle or polygon by computing the normal and the distance to the plane.

The dot product method is defined with the ^ operator. The method passes two vec-tors in as parameters and calculates the cross product between the two.

NOTE

NOTE

The cross product is used to gener ate a plane for a polygon or triangle.

 inline const cVector3 operator^(cVector3 const &a, cVector3 const &b) {    return cVector3    (       (a.y*b.z-a.z*b.y),       (a.z*b.x-a.x*b.z),       (a.x*b.y-a.y*b.x)     ); } 

Unit Vectors

Just as you normalized colors in Chapter 2, you can normalize vectors as well. The idea behind normalizing vectors is to change the components of the vector such that the direction is maintained , but the overall magnitude or length is 1.0. During normalization, the length from the origin changes, but the direction does not. To find the magnitude to a vector, you simply divide each (x, y, and z) component into itself (or internal magnitude) to generate a magnitude of 1.0. For example, V(255,0,0) normalized will be converted to ( V(255 / 255, 0, 0 ) = Unit Vector ( 1,0,0) ) , similar to how you normalize colors.

Normalizing a Vector

To normalize a vector and make it less than one, you must divide the magnitude by one and then scale each (x, y, and z) component down using this value.

 inline void cVector3::Normalize() {    float invMag = 1.f/Mag();    (*this) *=  invMag; } 

[ LiB ]


Focus On Photon Mapping
Focus On Photon Mapping (Premier Press Game Development)
ISBN: 1592000088
EAN: 2147483647
Year: 2005
Pages: 128
Authors: Marlon John

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net