Skybox

These notes assume you are loading the skybox from a .x file. There is one in the SDK media directory you could use and plenty available on the Internet.

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:

 

1

2

3

4

1

Right.x

Up.x

Look.x

0.0f

2

Right.y

Up.y

Look.y

0.0f

3

Right.z

Up.z

Look.z

0.0f

4

-position.right

-position.up

-position.look

1.0f

Render Steps

The view matrix is set each time the camera moves so when we come to render our skybox we need to:

  • Remember the current view matrix. To do this we can use the GetTransform function.
  • Change its position so the skybox is centred over us
  • Set the world matrix to identity
  • Render the skybox as we would for a normal .x file
  • Restore the original view matrix

Partial Example

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
D3DXMatrixIdentity(&matWorld);
gD3dDevice->SetTransform( D3DTS_WORLD, &matWorld);

// ... Render our skybox as for an x file

// Restore the original view matrix
gD3dDevice->SetTransform( D3DTS_VIEW, &matViewSave );

Other Issues

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 );

Design Note

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.

Advanced Note

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.



© 2004-2016 Keith Ditchburn