Realtime Voxel Landscape Engines - Part 3 - Texture and Lighting
by (21 February 2000)
|Return to The Archives
|Now we are able to determine which part of the landscape projects onto which part of the screen, we must find an efficient way of texturing all our spans.
The Ideal Texturing Scheme
|We could assign a small texture (say 32x32 for example) to each column of voxels. This way we would be able to texture the whole landscape with a very big unique texture. This is obviously not possible since memory requirements would be too large, over 1 GB for a 1024x1024 map! So we need to find a memory efficient way of texturing a whole landscape without tiling being too noticeable. The best way to do this is to use parametric texturing via a shader. This basically means that for each voxel, or small group of voxels, we store a small script that describes how to texture them. A simple script for example would blend two textures given an alpha coefficient chosen by height, and modulate the result with a lightmap. We could store an array of 256x256 shader scripts, that would produce some extremely good looking landscapes, but we would take a relatively important speed hit. The answer is to have the same shader script for the whole landscape, but store slightly different parameters in a corresponding array.
One layer of the shader script I use in terraVox is a lightmap. This is simply an array of colours the same size as the heightmap. So we can associate a colour to each column of voxels. I use greyscale values to simplify the code and reduce the thrashing of the cache while rendering. By using bilinear interpolation, we can find the colour corresponding to absolutely any point in the map. Then when we just interpolate that colour along the vertical span and use that to modulate whatever layers we draw over the top.
The algorithm that updates the lightmap is based on raytracing. For each point in the map, we interpolate along the ray towards the sun, and stop if we hit any mountains. We can then easily determine if the point is in a shadow or not. We associate a colour given the slope at that point, and a random coefficient of small magnitude.
Preparing the lightmap can be quite expensive computationally, so it's best to do it before hand. However, it is possible to update the lightmap in realtime by using a partial update scheme whereby blocks of 32x32 are updated each frame. You could also apply the same scheme only to the blocks within the view frustrum, but that would produce nasty artefacts when the view rotates suddenly.
The Texture Reference Map
The other two layers of the shader script used by terraVox are two textures that are chosen and mixed together given a texture reference map, which I call ref-map for short. The ref-map has the same format as the light-map, namely a bitmap of greyscale values.
We also need a few textures, sorted by index.
By looking up the value of the ref-map, we can easily select the two textures which we must blend, and what alpha coefficient we must use. As an example, let us consider a small area of the landscape, and what part of each texture is used. Note that the algorithm doesn't deal with the texture when the alpha coefficient is zero, since all black areas of the alpha blended textures are not needed.
The major disadvantage with this method is that we won't be able to have an area in the landscape where the textures blend from tex1 to tex4 for example. But we can however get an extremely fast inner loop, which is why I chose this method.
The Span Renderer
This pseudo-code should be integrated into the previous algorithm.
Standard techniques to increase the quality of the texturing could easily be inserted here.