Direct3D can apply geometric transforms to incoming data. If the hardware supports hardware transform and lighting, performance will be very good because computations will be processed on the GPU. Older boards will perform software transforms.
As with OpenGL, the camera is just a special-case transform, which can be accessed comfortably with some macro operations. Similarities with OpenGL do not end there. Under Direct3D, we have a projection matrix (which controls the way data is projected from a 3D game world to a 2D screen) and a modelview matrix, which controls the camera placement with regard to the scene. In addition, Direct3D supports a third transform called the world transform, which is used to apply local transforms to objects in the scene.
To begin with, here is the source code required to place a camera in the game world. First, we set our viewing matrix:
D3DXMATRIX matView; D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(x,0,z), &D3DXVECTOR3(x+cos(yaw),0,z+sin(yaw)), &D3DXVECTOR3(0,1,0)); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
Notice how we have implemented the classic first-person shooter camera: a position and a yaw vector. Now we need to specify the projection matrix as well. So, we set the field of view, the aspect ratio, and the near and far clipping planes:
D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.3, 1, 10000 ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
Once we have done this, we only need to learn how to specify hierarchical transforms in our game geometry. These are specified by a set of calls in the DIRECT3DX utility library, which is DirectX's counterpart to the GLU in OpenGL. Each function call returns a specific matrix, which can be multiplied by our world matrix. We start with the following call, which resets the world matrix:
D3DXMATRIX matWorld; D3DXMatrixIdentity(&matWorld);
Then, we can use any of the following:
D3DXMatrixRotationX, D3DXMatrixRotationY, D3DXMatrixRotationZ D3DXMatrixTranslation
Direct3D also supports some exotic matrices, such as a roll, pitch, and yaw matrix that is specified by the following code, which can be handy for flight simulators:
D3DXMATRIX* D3DXMatrixRotationYawPitchRoll( D3DXMATRIX* pOut, FLOAT Yaw, FLOAT Pitch, FLOAT Roll, );
As you might expect, matrices can be stacked hierarchically, so complex coordinate systems can be built. All we have to do is compose matrices to achieve the desired result. Take a look at the following example in which we render a robot arm with three transforms: one for the shoulder, one for the elbow, and one for the wrist:
D3DXMATRIX matWorld; D3DXMatrixIdentity(&matWorld); g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); // compute shoulder transform here g_pd3dDevice->SetTransform(D3DTS_WORLD, shoulder_transform); // render the upper arm, compute elbow transform g_pd3dDevice->MultiplyTransform(D3DTS_WORLD, elbow_transform); // render the lower arm, compute wrist transform g_pd3dDevice->MultiplyTransform(D3DTS_WORLD, wrist_transform); // render the robot arm
DirectX supports matrix concatenation much like OpenGL, so we can express a complex transform in terms of a series of simpler operations.