The skybox is defined in the x file as a cube with its insides textured with the sky and some sea. A skybox can therefore be loaded in exactly the same way as other .x files (see X Files ). The difference in implementation comes in the render function.
We want our skybox to always appear in the distance, therefore it should always be the same distance from the camera. For this reason you can think of the skybox as being a box positioned over the viewers head. So when we come to render the skybox we need to position the viewer to be bang in the centre of the box. As we know from our Direct3D matrices (see Matrices ) this information is held in the view matrix. The view matrix is split up like this:
The view matrix is set each time the camera moves so when we come to render our skybox we need to:
D3DXMATRIX matView, matViewSave; matWorld
// Remember the old view matrix
gD3dDevice->GetTransform( D3DTS_VIEW, &matViewSave );
// Alter the view matrix so its centred for the skybox
// The -0.4f for the y value is just to lower the horizon slightly - requires experimentation
matView = matViewSave;
matView._41 = 0.0f; matView._42 = -0.4f; matView._43 = 0.0f;
gD3dDevice->SetTransform( D3DTS_VIEW, &matView );
// Set a default world matrix
gD3dDevice->SetTransform( D3DTS_WORLD, &matWorld);
// ... Render our skybox as for an x file
// Restore the original view matrix
gD3dDevice->SetTransform( D3DTS_VIEW, &matViewSave );
When rendering the skybox we know it is the most distant thing we will draw so we could disable the Z buffer checks and also the z buffer writes (see also the advanced note below). Note that disabling z buffer checks (D3DRS_ZENABLE) only works if we draw the skybox first. We also do not want the skybox to be lit (its textures encode lighting), so we turn lighting off. We must remember to enable these things after we have finished rendering our skybox, to disable them:
// Disable the z buffer
gD3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
gD3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
// Disable lighting
gD3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
It is a good idea to apply bilinear filtering or otherwise your skybox will look blocky. You do this by setting the sampler states. You may as well just set them once after initialising Direct3D as it is good to apply this filtering to every graphic in your world.
You may also see a line at the edge of the texture, this is due to the way the texture addressing works. You can either set it to wrap or to clamp e.g.
// Clamp texture co-ordinates to get rid of horrible seam
gD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
gD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
The skybox is a 3D model but requires a different rendering approach. For this reason it makes sense to simply inherit it from a 3D object entity and redefine the render function.
It may seem best to simply render the skybox first and then the rest of the world, however if your game is fill rate limited (the time taken to draw to the screen is the limiting factor) it might be better to draw it last. If drawing last you will need to keep the z buffer tests but not the z buffer writes. By drawing it last only the visible parts will be drawn saving your fill rate. This is only an optimisation if you are fill rate limited so you need to do some timing tests before trying this.