Direct3D requires 3 matrices to be set before 3D geometry can be rendered. These matrices are used to transform the 3D geometry from its initial definition in model space into the final image drawn on the screen in 2D screen space.
The steps the geometry goes through are:
So the three matrix:
Direct3D provides many functions to help you create these matrices. Some are described below along with common settings and examples.
OpenGL and Direct3D vary in axis system. Direct3D uses a left handed system where X is right, Y up and Z in to the screen while OpenGL has X right, Y up and Z out of the screen. To convert between the two systems you can invert any one of the axis (but only one). I normally invert Z but inverting either of the others is also valid. You cannot convert from one of these coordinate systems to another using rotations.
Note: these are the two most commonly used coordinate systems but there are others e.g. 3DS Max has Z up Y into the screen and X right. To convert from left handed to the 3DS system you swap Z and Y coordinates.
Direct3D defines a matrix type called D3DXMATRIX. This is a structure that contains 4 by 4 elements for the matrix values. You can declare one simply:
D3DXMATRIX worldMatrix;
This creates a variable of type D3DXMATRIX and calls it worldMatrix.
There are many functions in Direct3D you can call that can alter matrix in certain ways. One important one is D3DXMatrixIdentity, this sets a matrix to be an identity matrix. It is often a good idea to initialise a matrix to a default identity. An identity matrix if applied in a transform leaves the 3D data unchanged, so to set the above declared matrix to identity you would do this:
D3DXMatrixIdentity( &worldMatrix );
Your world matrix is used to transform from model space into world space. You use it to take your 3D models and position, scale and rotate them into the world. Model space is the space you created your model in e.g. in an art package where 0,0,0 is normally the centre of the object and vertex positions are defined relative to this. You want to place this object into your game world so you need to translate, rotate and scale it into world space. There are a number of helper functions that you can use to create this matrix:
D3DXMatrixTranslation(D3DXMATRIX *out, FLOAT x, FLOAT y, FLOAT z );
This function takes a pointer to a matrix as its first parameter and then 3 values used to position the object in the world. So if you want your object to be located at 10,0,50 you would code:
D3DXMatrixTranslation(&matWorld,10.0f,0.0f,50.0f);
After this call the matrix is filled with the values required to carry out this transform.
D3DXMatrixRotationX(D3DXMATRIX *pOut, FLOAT angle)
D3DXMatrixRotationY(D3DXMATRIX *pOut, FLOAT angle)
D3DXMatrixRotationZ(D3DXMATRIX *pOut, FLOAT angle)
These three functions allow you to set up your matrix to rotate the object around one of the axis. You use the function you require and specify the rotation angle in radians. E.g. to rotate a object 180 degrees around the Y axis we could write:
D3DXMatrixRotationY(&matWorld,D3DX_PI);
D3DX_PI is a defined value for PI, PI is 180 degrees in radians. See the note at the end of this section about converting between them: Radians and Degrees
The final thing you can do with your world matrix is specify a scale. So you could make your object twice as big by scaling it with 2,2,2.
D3DXMatrixScaling(D3DXMATRIX *out, FLOAT sx, FLOAT sy, FLOAT sz );
Again you pass in the address of your matrix and then specify a scaling value, so to make your 3D object twice as big you could do this:
D3DXMatrixScaling(&matWorld, 2,0f,2,0f,2.0f);
Using the above functions you can position, rotate and scale your 3D object into your world. There is one important thing to note however and that is that you must do these matrix operations in a certain order. In Direct3D you must combine your matrix in SRT order (scale * rotation * translation).
Therefore if you want to position your entity at position pos and you have three rotations for: around the x axis (pitch), around the y axis (yaw) and around the z axis (roll) you could do it like this:
D3DXMATRIX matRotX,matRotY,matRotZ,matTrans;
// Calculate rotation matrix
D3DXMatrixRotationX( &matRotX, rot.x ); // Pitch
D3DXMatrixRotationY( &matRotY, rot.y ); // Yaw
D3DXMatrixRotationZ( &matRotZ, rot.z ); // Roll
// Calculate a translation matrix
D3DXMatrixTranslation(&matTrans,m_position.x,m_position.y,m_position.z);
// Calculate our world matrix by multiplying the above (in the correct order)
D3DXMATRIX matWorld=(matRotX*matRotY*matRotZ)*matTrans;
// Set the matrix to be applied to anything we render from now on
device->SetTransform( D3DTS_WORLD, &matWorld);
The view matrix transforms from world space into view space. It allows you to specify a position in the world where the view will be seen from. This needs to be set whenever the camera position is changed.
Camera control is in a separate topic (see camera), here I will just describe a simple method for creating the view matrix. The function builds a left handed view matrix from some inputs:
D3DXMatrixLookAtLH(D3DXMATRIX *out, CONST D3DXVECTOR3 *eye, CONST D3DXVECTOR3 *at, CONST D3DXVECTOR3 *up );
An example of creating and setting a view matrix to represent the camera positioned at 0,3,-5 looking at 0,0,0 and upright (0,1,0):
// Initialise our vectors
D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
// Get D3DX to fill in the matrix values
D3DXMATRIX matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
// Set our view matrix
device->SetTransform( D3DTS_VIEW, &matView );
Note: when implementing a camera system you will not want to use this function, see the camera notes.
The projection matrix specifies how our 3D view data is transformed onto our 2D screen. This is where we can specify the perspective to use. We use the following function that builds a projection matrix for us:
D3DXMatrixPerspectiveFovLH(D3DXMATRIX *out, FLOAT fovY, FLOAT Aspect, FLOAT zn, FLOAT zf );
Note: this matrix is normally only set once in a game, at the start unless you are doing any fancy effects or techniques like rendering to a texture.
An example of creating and setting this matrix might be:
D3DXMATRIX matProj;
FLOAT fAspect = ((FLOAT)800) / 600;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, 1.0f, 1000.0f );
gD3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
PI radians is 180 degrees. 2 PI is 360 degrees. Often it is easier to think in terms of degrees rather than radians. To convert between the two you could create two macros in a header file, like this:
#define DEGTORAD(degree) ((D3DX_PI / 180.0f) * (degree)) // converts from degrees to radians
#define RADTODEG(radian) ((180.0f / D3DX_PI) * (radian)) // converts from radians to degrees
Then you could rewrite the world matrix rotation as:
D3DXMatrixRotationY(&matWorld,DEGTORAD(180))
There are three matrix used by Direct3D to transform your 3D models into the final 2D image you see on the screen. They are the World Matrix, the View Matrix and the Projection Matrix. Note: If you are coming from a OpenGL background OpenGL combines the first two together.
You create your 3D model in Object Space. You apply a world matrix to transform from object space into world space. To take the camera into account you apply a view matrix to transform from world space into view space and finally you apply a projection matrix to transform from view space into screen space.
Direct3D provides a number of functions that allow you to easily create your matrix.
The world matrix needs to be set to position every entity you render
The view matrix is set each time the camera changes position or orientation
The projection matrix is set just once at the start of your game