Skybox tutorial

From Sidvind
Jump to: navigation, search

Preface[edit]

This article is assumes you have some basic understanding of OpenGl.

In many 3D-games today we see an image in the background. The image is nicely rotated when we look around. There are some different methods to produce this effect but the skybox is the easiest and probably the best. A skybox is a box which seems to contain the entire scene and is rendered from the inside. The skybox is always rendered before the other geometry in the scene with depthtesting off.

Theory[edit]

Geometry[edit]

The geometry of the skybox is simple, just a box. However, keep in mind that we are inside the box and the sides must face us. We will be using GL_QUADS to make the box. The box will be 1x1x1 units in size. We never want to go to the edge of the box so we will use a little trick, we position the camera in the middle of the box each time it is rendered. Also, make sure that depthtesting is disabled, lighting should also be disabled but some neat day/night effect maybe could be produced by using lighting, your call.

Texture[edit]

Fig 1. 6 textures added as an unwrapped box

The next thing is the texture. A skybox could contain either 5 or 6 textures depending on whenever the bottom is visible or not. The texture itself should be divided to 5 or 6 parts, one for each quad.

The image below is an unwrapped box, this is an example of how you could wrap your textures. It doesn't really matter as long as you know which one that belongs the each quad. To generate my textures I use Terragen (See below). One issue we might run into is edges in corners, to prevent this we tell OpenGL to use GL_CLAMP as texturewrapper. This should pretty much conclude everything about a basic skybox, so onto the code.

Simple skybox[edit]

As I alread said I assume some knowledge in OpenGL so you should know how to make the box itself, just remember that we are inside the box. In my examples I have 6 quads. First I will show you how to load your textures: <codebox title="Load a texture"> glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); </codebox>

As explained above this makes sure the textures displayed correctly and no edges will appear on the quads. In some cases we need to turn off interpolation too. Just set GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER to GL_NEAREST. Below is a sample routine to render the skybox. All lines will be explained later.

<codebox title="Rendering">

   // Store the current matrix
   glPushMatrix();
   // Reset and transform the matrix.
   glLoadIdentity();
   gluLookAt(
       0,0,0,
       camera->x(),camera->y(),camera->z(),
       0,1,0);
   // Enable/Disable features
   glPushAttrib(GL_ENABLE_BIT);
   glEnable(GL_TEXTURE_2D);
   glDisable(GL_DEPTH_TEST);
   glDisable(GL_LIGHTING);
   glDisable(GL_BLEND);
   // Just in case we set all vertices to white.
   glColor4f(1,1,1,1);
   // Render the front quad
   glBindTexture(GL_TEXTURE_2D, _skybox[0]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 0); glVertex3f(  0.5f, -0.5f, -0.5f );
       glTexCoord2f(1, 0); glVertex3f( -0.5f, -0.5f, -0.5f );
       glTexCoord2f(1, 1); glVertex3f( -0.5f,  0.5f, -0.5f );
       glTexCoord2f(0, 1); glVertex3f(  0.5f,  0.5f, -0.5f );
   glEnd();
   // Render the left quad
   glBindTexture(GL_TEXTURE_2D, _skybox[1]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 0); glVertex3f(  0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 0); glVertex3f(  0.5f, -0.5f, -0.5f );
       glTexCoord2f(1, 1); glVertex3f(  0.5f,  0.5f, -0.5f );
       glTexCoord2f(0, 1); glVertex3f(  0.5f,  0.5f,  0.5f );
   glEnd();
   // Render the back quad
   glBindTexture(GL_TEXTURE_2D, _skybox[2]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 0); glVertex3f( -0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 0); glVertex3f(  0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 1); glVertex3f(  0.5f,  0.5f,  0.5f );
       glTexCoord2f(0, 1); glVertex3f( -0.5f,  0.5f,  0.5f );
   glEnd();
   // Render the right quad
   glBindTexture(GL_TEXTURE_2D, _skybox[3]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 0); glVertex3f( -0.5f, -0.5f, -0.5f );
       glTexCoord2f(1, 0); glVertex3f( -0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 1); glVertex3f( -0.5f,  0.5f,  0.5f );
       glTexCoord2f(0, 1); glVertex3f( -0.5f,  0.5f, -0.5f );
   glEnd();
   // Render the top quad
   glBindTexture(GL_TEXTURE_2D, _skybox[4]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 1); glVertex3f( -0.5f,  0.5f, -0.5f );
       glTexCoord2f(0, 0); glVertex3f( -0.5f,  0.5f,  0.5f );
       glTexCoord2f(1, 0); glVertex3f(  0.5f,  0.5f,  0.5f );
       glTexCoord2f(1, 1); glVertex3f(  0.5f,  0.5f, -0.5f );
   glEnd();
   // Render the bottom quad
   glBindTexture(GL_TEXTURE_2D, _skybox[5]);
   glBegin(GL_QUADS);
       glTexCoord2f(0, 0); glVertex3f( -0.5f, -0.5f, -0.5f );
       glTexCoord2f(0, 1); glVertex3f( -0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 1); glVertex3f(  0.5f, -0.5f,  0.5f );
       glTexCoord2f(1, 0); glVertex3f(  0.5f, -0.5f, -0.5f );
   glEnd();
   // Restore enable bits and matrix
   glPopAttrib();
   glPopMatrix();

</codebox>

Fig 2. The skybox is rendered in the background with 3 crates and a grid

The camera transformation is a little bit special when rendering a skybox. We never translates by the camera position, only rotation is applied. This is because the skybox never moves. It is always positioned at origo (0,0,0). Below is a sample of the skybox in use. The rendering above could easily be optimized in numerous ways but for clarity it is kept simple.

Tips&Tricks[edit]

There are many things that can improve the quality of the skybox. Here are a few:

Moving clouds[edit]

Create a plane inside the skybox, let it have transparent edges and a texture with transparent clounds. Animate the texture coordinates in a given direction.

Sun[edit]

Add a lightsource in the spot that the sun would be and add a large lensflare. Alphablending a white fullscreen quad when you look toward it really makes you think you look into the sun.

Day & night[edit]

Either animate the texture or very slowly fade between two skyboxes. Also remember to adjust the ambient lighting to make it darker at night.

Terragen[edit]

Terragen is a tool to generate terrains but it works perfectly for generating skybox-textures too. I won't go into details but this is the basics. Position the camera in the center. Make sure the zoom is 1.0 or the image won't fit together. Adjust roll, yaw and pitch for each image you take. The Valve Developer Community has a nice tutorial and automated script for generating textures with terragen.

Links[edit]