MULTIMEDIA

Programming with DirectX : Game Math - Vectors

2/8/2011 4:12:49 PM
A vector is a mathematical structure that is used to represent a direction. There are different types of vectors, and the most common kind you will see in computer graphics are 2D, 3D, and 4D vectors. The type of vector determines the number of axes or dimensions it represents. Therefore, a 3D vector is a vector that exists in 3D space and has X, Y, and Z axes.

At its heart a vector is a direction, and in computer graphics the structures used to define vectors are often used to represent positions as well. Taking a 3D vector as an example, the X, Y, and Z axes can be used to mark a position just as they can mark a direction. The difference lies in what the programmer intends to use the data for. In math and physics a vector is an object with a direction and a length. A simple visual of this is shown in Figure 1, where the vector moves from the origin (the origin in 3D space has an X axis of 0, Y of 0, and Z of 0) along the positive Y axis 10 units, making its final position X:0, Y:10, Z:0. The direction of the vector is

Figure 1. A simple look at a vector as a direction and a length.


X:0, Y:1, and Z:0. Since the vector only moves long the Y axis (Y:1), only the Y has a value.

In this article we’ll look at 3D vectors, but keep in mind that everything discussed here also applies to 2D and 4D vectors. A 2D vector is made up of X and Y axes, and a 4D vector is made up of X, Y, Z, and W axes. The Direct3D 3D vector is called D3DXVECTOR3, and it has the following structure according to the DirectX 10 documentation.

typedef struct D3DXVECTOR3 {
FLOAT x;
FLOAT y;
FLOAT z;
} D3DXVECTOR3, *LPD3DXVECTOR3;

This structure can be used to represent both positions and directions. In the case of a vertex, this structure is often used to represent the position attribute of each vertex of a primitive. In the upcoming subsections of this discussion on vectors, we will briefly discuss a few of the most common mathematical operations performed on vector objects. To view a complete list of the mathematical functions offered by the DirectX SDK vector objects, refer to the DirectX documentation.

Vectors are used for all types of mathematical objects, especially in game physics. This includes directions, positions, tensors, pseudovectors, and other vector-like objects. In other words, if it has an X, Y, and Z property (using 3D vectors as an example), then most likely programmers will use their vector code to represent it rather than creating another structure with the same properties (member variables). This makes it easier to get started if you do not have strong math skills.


Vector Addition and Subtraction

The first operations we’ll look at in vector mathematics are adding and subtracting. Mathematically, adding and subtracting are very elementary. To add two vectors together, again using 3D vectors as an example, you add each of the matching axes together, and the result is stored in a new vector holding the solution. In other words, you take the X axis from vectors A and B and add them together and store answer in a result vector’s X axis. You do the same with the Y and Z axes. This is shown in the following example.

Vector3D A = (10, 5, 8)
Vector3D B = (3, 1, 11)

Vector3D Result = A + B

or

Result = (13, 6, 19)

or

Result.x = A.x + B.x
Result.y = A.y + B.y
Result.z = A.z + B.z

Using the vectors from the addition example, subtraction is the same, but instead of adding you are literally subtracting each axis from its matching counterpart in the other vector. If vector A is (10, 5, 8) and vector B is (3, 1, 11), the resulting vector when subtracting is (7, 4, –3).

Vectors in DirectX are added using the function D3DXVec3Add() and subtracted using the D3DXVec3Subtract function. These functions have the following function prototypes where the function returns the result as a vector. The first parameter is the address for the vector that will store the result of the operation, the second parameter is the first vector in the operation (vector A), and the last parameter is the second vector in the operation (vector B).

D3DXVECTOR3 * D3DXVec3Add(
D3DXVECTOR3 * pOut,
CONST D3DXVECTOR3 * pV1,
CONST D3DXVECTOR3 * pV2
);

D3DXVECTOR3 * D3DXVec3Subtract(
D3DXVECTOR3 * pOut,
CONST D3DXVECTOR3 * pV1,
CONST D3DXVECTOR3 * pV2
);

The result from these functions can be obtained by their return value or by passing the address of the object to hold the result in the first parameter. Alternatively, you can use the structure’s overloaded operators to perform addition and subtraction instead of calling these functions. This would result in the following in code.

D3DXVECTOR3 vectorA, vectorB, result;
result = vectorA + vectorB;
result = vectorA - vectorB;

Vector Normalization

The length of a vector is called its magnitude. To find the length of a vector you multiply each component of a vector with itself and add all of the axes. The square root of this result is the magnitude of the vector. This is shown in the following pseudo-code example.

length = square_root(vector.x * vector.x + vector.y * vector.y +
vector.z * vector.z)

This equation gives you the inner product. The square root of this is the length of a vector. When a vector has a length that equals 1, it is said that the vector is unit-length. Another term for this is normalized vector (normal for short). The length itself is a floating-point value.

A normal has many uses in video game development. Later in this book you’ll see how normal vectors contribute to the lighting equation. In this article we will briefly discuss how to convert a vector to a normal.

To convert a vector to a normal, the first step is to find the vector’s length. If the length equals 1, the vector is already unit-length, and nothing else needs to be calculated. If the length is not 1, you can divide each axis of the vector by the length, which will result in scaling the vector to unit-length. This is done as follows.

D3DXVECTOR3 vectorA, normal;
float length = D3DXVec3Length(&vectorA);
normal = vectorA / length;

The D3DXVec3Length() function can be used to find the length of a D3DXVECTOR3 object. Alternatively, you can normalize a vector by calling the DirectX function D3DXVec3Normalize(), which takes as parameters the address of the vector that will store the result of the operation and the vector to normalize. Normalized vectors are used for many mathematical equations, such as lighting for example. The function prototype for the D3DXVec3Normalize() function is shown as follows.

D3DXVECTOR3 * D3DXVec3Normalize(
D3DXVECTOR3 *pOut,
CONST D3DXVECTOR3 *pV
);

Common Additional Vector Operations

We’ll look at a few other vector operations in this article that will come up later in this book in discussions of various topics. These operations include the following.

  • Dot product

  • Cross product

  • Lerp

The dot product is result of multiplying two vectors and adding the resulting axes. The dot product of two vectors can be found as follows using 3D vectors as an example.

float dot = vectorA.x * vectorB.x + vectorA.y * vectorB.y + vectorA.z *
vectorB.z;

The cross product of two vectors, put simply, is obtained by cross multiplying the axes of one vector with the axes of another. The cross product is used to find a vector that is perpendicular to two source vectors, which can be useful when you need such a vector in relation to two other vectors. The cross product, also known as the vector product, is shown below, where the axes that are multiplied are cross multiplied with one vector to another.
cross.x = vectorA.y * vectorB.z - vectorA.z * vectorB.y
cross.y = vectorA.z * vectorB.x - vectorA.x * vectorB.z
cross.z = vectorA.x * vectorB.y - vectorA.y * vectorB.x

Lerp is short for linear interpolation. It is an operation that is used to find a value that lies somewhere between two source values. The idea is to take a start value, an end value, and a percentage from 0.0 to 1.0 (i.e., 0 to 100%). If the percentage supplied is 0.0, the start vector is returned. If the percentage is 1.0, the ending value is returned, but if the percentage is a value between the two, a vector that lies in the percentage between the two will be returned.

For example, using single values, let’s say we have a start value of 6 and an end value of 18. If we supply a percentage of 50%, that is like saying what is 50% into the range of 6 and 18. The answer is 12, since 12 lies halfway between 6 and 18. The same concept is used for linear interpolation with vectors, but this concept is applied to each axis of the vector. When using linear interpolation, you are linearly finding a value between two vectors based on the percentage, which often has the notation t in game development books. The equation for finding the linear interpolated vector between vector A and B is shown in the following example.

result = (vectorB - vectorA) * percentage + vectorA

The equation for the linear interpolation is quite simple. It works by finding the range total between values (vectors) A and B, multiplying that by the percentage (t), and adding that to the starting vector. So if we wanted to lerp between the values 13 and 57 by 0.4 (40%), we would first find the range (57 – 13, which equals 44) and then multiply 44 by 0.4, which is 17.6. We would then add 17.6 to the starting value to get the value that lies 40% into the range, which would result in 30.6.

Linear interpolation is sometimes used for animations, where time is used as t. So if an animation had to occur within a certain time frame, for example, you could interpolate between two vertex positions to find where the vertex would be at a specific time. If you do this for all vertices in a model, you get the type of animation used in many early 3D video games before bone animation (discussed later in the book) became the standard.

DirectX 3D Vector Functions

The DirectX SDK offers a number of functions for vector objects. In this section we will look briefly at the 3D vector functions. The 2D and 4D vectors have equivalent functions, although a few 3D functions do not have a 2D or 4D counterpart. For example, there is no D3DXVec2Cross(). Table 1 lists the 3D vector functions in the DirectX SDK.

Table 1. The 3D Vector Functions from the DirectX SDK
FunctionDefinition
D3DXVec3Add()Vector addition
D3DXVec3BaryCentric()Returns a point in Barycentric coordinates
D3DXVec3CatmullRom()Performs CatmullRom interpolation
D3DXVec3Cross()Performs the cross product of two vectors
D3DXVec3Dot()Performs the dot product between two vectors
D3DXVec3Hermite()Performs Hermite spline interpolation
D3DXVec3Length()Calculates the length (magnitude) of a vector
D3DXVec3LengthSq()Calculates the square of the vector’s length
D3DXVec3Lerp()Performs linear interpolation
D3DXVec3Maximize()Finds the maximum vector of two source vectors
D3DXVec3Minimize()Finds the minimum vector of two source vectors
D3DXVec3Normalize()Normalizes a vector to unit-length
D3DXVec3Project()Projects a vector from object space to screen space
D3DXVec3ProjectArray()Projects a float array from object space to screen space
D3DXVec3Scale()Scales a vector
D3DXVec3Subract()Vector subtraction
D3DXVec3TransformArray()Transforms a float array by a matrix
D3DXVec3TransformCoord()Transforms a vector by a matrix and projects back into the w = 1
D3DXVec3TransformCoordArray()Transforms a float array by a matrix and projects back into the w = 1
D3DXVec3TransformNormal()Performs a 3 × 3 vector/matrix transformation to transform a normal vector by a matrix
D3DXVec3TransformNormalArray()Performs a 3 × 3 vector/matrix transformation to transform a normal vector represented as a float array by a matrix
D3DXVec3UnProject()Projects a vector from screen space back to object space
D3DXVec3UnProjectArray()Projects a vector represented as a float array from screen space back to object space
D3DXVec3Transform()Transforms a vector by a matrix

Planes

A plane can be thought of as an infinitely thin surface that extends forever alone two axes. Planes have many uses in video games, many of which fall under the subject of collision detection, where a plane can be used to test if an object of some type travels from one side of the plane to the other.

Planes are not rendered; they are used mathematically for tests. These tests are essentially set up to test which side of the plane an object is on or if the object penetrates the plane. Take, for example, a game in which a cut scene is triggered when a player walks into a room. This can be done as simply as defining a plane and testing every frame to see if the player is on a different side of the plane than before. If so, the cut scene is triggered.

The plane equation is defined as ax + by + cz + dw = 0. In code a plane can be defined as a structure with coefficients a, b, c, and d. These coefficients are usually floating-point values. In DirectX the D3DXPLANE structure is defined as follows:

typedef struct D3DXPLANE {
FLOAT a;
FLOAT b;
FLOAT c;
FLOAT d;
} D3DXPLANE, *LPD3DXPLANE;

You can think of a plane as a normal that is defined by the first three coefficients, a, b, and c, and a distance defined by the last coefficient d. You can manually specify this information or you can create a plane from a primitive or surface. It is very common to create a plane out of a triangle and then use that plane for some purpose such as collision detection.

Plane Operations

The DirectX SDK has several functions that can be used with plane objects. The definition of these functions (see Table 2) can give you an idea of what you can do with planes.

Table 2. DirectX SDK Plane Object
FunctionDefinition
D3DXPlaneDot(const D3DXPLANE *pP, const D3DXVECTOR4 *pV)Computes the dot product of a plane and a 4D vector.
D3DXPlaneDotCoord(const D3DXPLANE *pP, const D3DXVECTOR3 *pV)Computes the dot product of a plane and a 3D vector. This is the same as the D3DXPlaneDot() function but assumes a w of 1.
D3DXPlaneDotNormal(const D3DXPLANE *pP, const D3DXVECTOR3 *pV)The same as D3DXPlaneDotCoord() but assumes a w of 0.
D3DXPlaneFromPointNormal(D3DXPLANE *pP, const D3DXVECTOR3 *pPoint, const D3DXVECTOR3 *pNormal)Computes a plane from a point and a normal.
D3DXPlaneFromPoints(D3DXPLANE *pP, const D3DXVECTOR3 *v1, const D3DXVECTOR3 *v2, const D3DXVECTOR3 *v3)Creates a plane from three points. This can be used to define a plane from a triangle.
D3DXPlaneIntersectLine (const D3DXVECTOR3 *pOut, const D3DXPLANE *pP, const D3DXVECTOR3 *v1, const D3DXVECTOR3 *v2)Tests to see if a line intersects with the plane. If it does, the point of intersection in 3D space is returned. The point v1 is the start of the line, and v2 is the end of the line. The pOut parameter stores the position of the intersection.
D3DXPlaneNormalize(D3DXPLANE *pOut, const D3DXPLANE *pP)Normalizes a plane so that its coefficients are unit-length. If the a, b, and c of a plane are its normal, this function essentially normalizes it.
D3DXPlaneScale(D3DXPLANE *pOut, const D3DXPLANE *pP, FLOAT s)Scales a plane by a specified amount.
D3DXPlaneTransform(D3DXPLANE *pOut, const D3DXPLANE *pP, const D3DXMATRIX *pM)Transforms a plane by a matrix (see the upcoming “Matrices” section).
D3DXPlaneTransformArray (D3DXPLANE *pOut, UINT OutStride, const D3DXPLANE *pP, UINT PStride, const D3DXMATRIX * pM, UINT n)Transforms an array of planes by a matrix.

Other  
  •  iPhone 3D Programming : Textures and Image Capture - Generating and Transforming OpenGL Textures with Quartz
  •  iPhone 3D Programming : Textures and Image Capture - The PowerVR SDK and Low-Precision Textures
  •  Building LOB Applications : Using Visual Studio 2010 WCF Data Services Tooling
  •  Building LOB Applications : Accessing RESTful Data using OData
  •  Programming with DirectX : Additional Texture Mapping - Image Filters
  •  Microsoft XNA Game Studio 3.0 : Making a Game Program
  •  iPhone 3D Programming : Textures and Image Capture - Texture Compression with PVRTC
  •  iPhone 3D Programming : Textures and Image Capture - Texture Formats and Types
  •  iPhone 3D Programming : Textures and Image Capture - Fight Aliasing with Filtering
  •  iPhone 3D Programming : Textures and Image Capture - Texture Coordinates Revisited
  •  Programming with DirectX : Additional Texture Mapping - Sprites
  •  Programming with DirectX : Additional Texture Mapping - Alpha Mapping
  •  Microsoft XNA Game Studio 3.0 : Writing Your First Program (part 2) - Running the Same XNA Game on Different Devices
  •  Microsoft XNA Game Studio 3.0 : Writing Your First Program (part 1)
  •  Programming with DirectX : Shading and Surfaces - Additional Texturing Topics
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 4) - Enabling Textures with ES2::RenderingEngine
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 3) - Enabling Textures with ES1::RenderingEngine
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 2) - Generating Texture Coordinates
  •  iPhone 3D Programming : Adding Textures to ModelViewer (part 1) - Enhancing IResourceManager
  •  Programming with DirectX : Shading and Surfaces - Implementing Texture Mapping (part 2) - Multi Texture Demo
  •  
    Most View
    Google Chrome 21 - Fast, Free Web Browser
    Nvidia GeForce GTX Titan 6 GB Graphics Card Review (Part 2)
    New Year Gift Guide 2013 (Part 5)
    Installing HP-UX : Setting the System Parameters After Booting
    View21 HD Set Top Box Review
    Ultimate Guide To Google Play (Part 2)
    Windows Vista : Setting Up a Small Network - Setting Up a Peer-to-Peer Network
    Enhancing Your Digital Life From The Desktop To Your Mobile (Part 3)
    Apple Macbook Pro With Retina Display
    Security - The Human Element (Part 1)
    Top 10
    Panasonic Lumix G Vario 14-42mm f/3.5-5.6 II Lens (Part 2)
    Panasonic Lumix G Vario 14-42mm f/3.5-5.6 II Lens (Part 1)
    Epson EH-TW8100 - Epson Introduces The Inbetweener
    Epson Stylus Photo 1500W A3+ Wi-Fi Printer
    OCZ Vertex 4 (256GB) - Hitting The SSD Sweet Spot
    Plustek OpticFilm 120 Scanner Review (Part 2)
    Plustek OpticFilm 120 Scanner Review (Part 1)
    Samsung 840 Series Pro 256GB 2.5 Inch SATA Solid State Drive
    SanDisk Ultra Plus 256GB - The Cheapest High-End SSD
    DrawPlus X6 – 64 Bit Support And A New Rendering Engine