Quake Sky

Background

This description of Quake's curved sky effect is in response to some questions about generating sky images:

Assume a sky "box" that is to appear infinitely far away. The box should be reasonably large and centered on the viewer. For a given vertex of the box (I tesselated a cube into quads, 8x8 per face is good enough), compute S, a direction vector to the vertex relative to the position of the viewer. Given S, compute a texture coordinate for the vertex as follows:

#define z_scale 4       // affects how "tall" the sky is
#define z_offset 0.707  // affects where the bottom of the sky is
#define t_scale 2       // larger numbers result in more copies of clouds
#define time_scale 0.07 // larger numbers result in faster cloud scrolling

S = normalize(S);
S.z = z_scale * (S.z + z_offset);
S = normalize(S);
S.x *= t_scale;
S.y *= t_scale;
S.x += time * time_scale;
S.y += time * time_scale;

Use S.x and S.y as the texture coordinates s and t respectively. Draw all the polygons of the sky box this way, using perhaps a texture borrowed from Quake, and you will see a Quake sky.

You can tweak the look of the sky by playing with z_scale, z_offset, and t_scale. That z_offset is sqrt(2) aligns the base of the sky with the bottom of the sky box, making it seem like the viewer is on the top of a mountain. A z_offset of 0 would place the base of the sky at the middle of the sky box. The sky's "base" is where the sky inverts. Below the base, the sky appears upside down. (See image (f), below.)

You don't have to use a box. Another shape (such as a sphere or dome of some sort) will also do. The shape you choose won't affect the image of the sky much, because of the way the texture coordinates are generated.

Images

I generated a bunch of images using the real-time programmable shading system we have been developing here at Stanford. Here is the shader I used:
surface shader floatv
sky (texref clouds, float time)
{
    floatv S = { Pobj[0], -Pobj[2], Pobj[1], 0 }; // [1] = up
    S = normalize(S);
    S = { S[0], S[1], 4 * (S[2] + 0.707), 0 };
    S = normalize(S);
    floatv uv_lo = S * { 2, 2, 0, 0 } + { time / 15 , time / 15, 0, 1 };
    floatv uv_hi = S * { 3, 3, 0, 0 } + { time / 15 , time / 15, 0, 1 };
    floatv Lo = texture(clouds, uv_lo);
    floatv Hi = texture(clouds, rotate(125, 0, 0, 1) * uv_hi);
    return Lo over Hi over { 0.6, 0.5, 1.0, 1.0 };
}

The cloud texture is a luminance-alpha texture with luminance premultiplied by alpha. The non-premultiplied luminance is a constant 1.0 everywhere, so the final luminance and alpha components are equal. The luminance and alpha are shown here:

A cloud image. This image was generated using a simple fractal technique, something like what is described here.

The shader implements two cloud layers (Lo and Hi) that are scaled differently and therefore look to be at different heights. It is easy to distinguish the layers when the clouds animate, but difficult to see them in the static images shown below. The clouds layers are composited over one another and also over a blue background.

(a)(b)
(c)(d)
(e)(f)
Three sky images. Images (b), (d), and (f) depict three views of the Quake sky. The wireframe images (a), (c), and (e) show the corresponding portion of the sky box. The images (e) and (f) show the "base" of sky and its alignment relative to the bottom of the sky box. Note that the tesselation of the cube faces is a bit high; an 8x8 tesselation would have worked just fine.


Copyright © 2000 Kekoa Proudfoot. All rights reserved. kekoa@graphics.stanford.edu