Skyboxes

Back to index

A skybox is a little trick to produce the distant background to a scene - maybe a distant landscape to the horizon, and a sky.

The obvious way to do this would be to draw very large and distant quads with suitable textures on them. But an easier way is this:

The rest of the scene would usually be 'outside' the skybox. But there will be no effect from it on the depth buffer, so everything else will be visible. An advantage of this approach i sthat you do not hav eto worry about drawing things outside the skybox.

An example

Suppose we want to draw a scene in space. We could Google for pictures of star fields, but in fact they are easy to create programmatically. We need 6, with some attributes:

    // size of the sky box buffers
    int skyBoxImageWidth = 512;
    int skyBoxImageHeight = 512;
    // the buffers
    private ByteBuffer[] skyBoxImage = new ByteBuffer[6];
    // their 'names'
    int[] skyBoxTextures = new int[6];

We make the buffer contents like this:

/**
 * Create the skyBoxImage buffers and put star images in them.
 * For each of the 6 buffers, make each texel black, and then
 * put 500 coloured dots in them which look like distant fixed stars
 * 
 */
    void makeSkyboxImages() {
        for (int image = 0; image < 6; image++) {
            for (int i = 0; i < skyBoxImageHeight; i++) {
                for (int j = 0; j < skyBoxImageWidth; j++) {
                    skyBoxImage[image].put((byte) (0));
                    skyBoxImage[image].put((byte) (0));
                    skyBoxImage[image].put((byte) 0);
                    skyBoxImage[image].put((byte) 255);
                }
            }
            skyBoxImage[image].rewind();
            // the texel at (col, row) is at offset
            // row*skyBoxImageWidth*4 + col*4 an dthe next 3 bytes
            // make 500 stars
            for (int i = 0; i < 500; i++) {
                int row = (int) (Math.random() * skyBoxImageHeight);
                int col = (int) (Math.random() * skyBoxImageWidth);
                int offset = row * skyBoxImageWidth * 4 + col * 4;
                skyBoxImage[image].put(offset, (byte) (155 + Math.random() * 100));
                skyBoxImage[image].put(offset + 1, (byte) (155 + Math.random() * 100));
                skyBoxImage[image].put(offset + 2, (byte) (155 + Math.random() * 100));
            }
        }
    }

We set this up in init()

 // make skybox images 
        for (int i = 0; i < 6; i++) {
            skyBoxImage[i] = BufferUtil.newByteBuffer(skyBoxImageWidth * skyBoxImageHeight * 4);
        }
        makeSkyboxImages();
        gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
        final int[] tmp = new int[6];
        gl.glGenTextures(6, tmp, 0);
        for (int image=0; image<6; image++)
        {
        skyBoxTextures[image] = tmp[image];
        // make the named texture to be the type we want
        gl.glBindTexture(GL.GL_TEXTURE_2D, skyBoxTextures[image]);
        // say how it repeats if needed
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
        // and how texels and pixels map
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                GL.GL_LINEAR);
        // connect to data
        gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, skyBoxImageWidth,
                skyBoxImageHeight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
                skyBoxImage[image]);
        } 

This draws the sky box:

private void drawSkyBox(GL gl)
    {
        float x=camera.getX();
        float y=camera.getY();
        float z=camera.getZ();
        gl.glDisable(GL.GL_DEPTH_TEST);
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
        gl.glBindTexture(GL.GL_TEXTURE_2D, skyBoxTextures[0]);
        gl.glBegin(GL.GL_QUADS);
        // one face..
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3f(x-1.0f, y-1.0f, z-1.0f);       
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex3f(x-1.0f, y+1.0f, z-1.0f);       
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex3f(x+1.0f, y+1.0f, z-1.0f);        
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex3f(x+1.0f, y-1.0f, z-1.0f);
        // another face..
        gl.glBindTexture(GL.GL_TEXTURE_2D, skyBoxTextures[1]);
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3f(x+1.0f, y-1.0f, z-1.0f);       
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex3f(x+1.0f, y+1.0f, z-1.0f);       
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex3f(x+1.0f, y+1.0f, z+1.0f);        
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex3f(x+1.0f, y-1.0f, z+1.0f);
        

        //.. plus another 4 faces
        gl.glEnd();
        gl.glDisable(GL.GL_TEXTURE_2D);
    }

The code in display is

gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();// Reset The View    
glu.gluLookAt(camera.getX(), camera.getY(), camera.getZ(), lookAt.getX(), lookAt.getY(), lookAt.getZ(), up.getX(), up.getY(), up.getZ());
drawSkyBox(gl);
gl.glEnable(GL.GL_DEPTH_TEST);
    
glut.glutSolidSphere(1.0, 20, 20);

..

The result is 1