Game Development Community

dev|Pro Game Development Curriculum

Terrain Generation Progress

by Demolishun · 09/03/2012 (6:07 pm) · 7 comments

Slowly but surely I am making progress on my terrain generation code. It took a while to understand which of the modern algorithms are generally employed for terrain creation. This is certainly a very interesting field unto itself. I know my brain is being challenged as I search for algorithms and practical implementations.

Despite the math part the hardest part so far has been to understand how to use the existing libraries for manipulating images. Most of the image libraries or image manipulation programs out there cannot handle 16 bit single channel greyscale images. Even the venerable GIMP will not be able to handle this format until version 2.10. That really surprised me. The reason this format is important is because most terrain programs spit out this format as a standardized format. You read that same file into GIMP and it squishes the data into an 8 bit format losing a lot of resolution.

I finally did find the libraries to do the manipulation and save to the proper formats:

  1. PythonMagick (an ImageMagick wrapper for Python, missing lots of features of ImageMagick unfortunately). This is the program I use to write the 16 bit image files for using in terrain importing and generation.
  2. Numpy/SciPy are two libraries I am using for handling the image data. I also intend to use the math functions there provide to do more advanced manipulation.
  3. Noise is a Python library that does some Perlin noise generation. I used it in various combinations to produce the terrain in the screenshots.

Here is some code that does the terrain generation (just a code snippet):
def ter_genPlasma():
    octaves = 4
    freq = 64.0 * octaves

    random.seed()
    val = random.randint(0,256)
    rptx = random.randint(128,16535)
    rpty = random.randint(128,16535)
    data = numpy.array([0]*512*512,dtype=numpy.float32)
    for y in range(512):
        for x in range(512):
            fn = ((pnoise2(x / freq, y / freq, octaves, repeatx=rptx, repeaty=rpty, persistence=0.5, base=val) + 1.0)/2.0)*4096.0
            data[(x+y*512)] = fn

    octaves = 1
    freq = 128.0 * octaves
    val = random.randint(0,256)
    rptx = random.randint(128,16535)
    rpty = random.randint(128,16535)
    for y in range(512):
        for x in range(512):
            fn = ((pnoise2(x / freq, y / freq, octaves, persistence=0.5, base=val) + 1.0)/2.0)*16384.0
            #fn = ((snoise2(x / freq, y / freq) + 1.0)/2.0)*16384.0
            data[x+y*512] += fn
            pass

    octaves = 8
    freq = 4.0 * octaves
    val = random.randint(0,256)
    rptx = random.randint(128,16535)
    rpty = random.randint(128,16535)
    for y in range(512):
        for x in range(512):
            fn = ((pnoise2(x / freq, y / freq, octaves, persistence=0.5, base=val) + 1.0)/2.0)*2048.0
            data[x+y*512] += fn
            pass

    #data2 = numpy.multiply(data,32767)
    odata = data.astype(numpy.int16)

    size = "%sx%s" % (512, 512)
    mdata = PythonMagick.Blob(odata.tostring())
    image = PythonMagick.Image(mdata, size, 16, 'A')

    image.magick("png")
    image.defineValue("png","bit-depth","16")
    image.defineValue("png","color-type","0")

    return image

Here are some screenshots that the above code is producing:
demolishun.net/images/torque/perlin_multi_layer_cam.png
demolishun.net/images/torque/perlin_multi_layer_fp.png
As I experiment with the noise functions and how to combine them I realize that I am going to have to take a multi layer approach. Terrain is a combination of different forces working together. I also have been looking at terrain and thinking about what it took to create that environment. So I am going to take a hint from nature and build up some working models for how terrain will be formed and get the computer to do the work.

Some of the concepts I am thinking about for the layering are as follows:

  1. Use a hardness map to determine the relative sub strata makeup of the terrain. Is it rock or dirt?
  2. Set a median point for manipulation of the terrain. Things above this point will be mountainous, things below this point will be ravines cut from a flat area like the grand canyon. Islands are generally all mountainous in nature.
  3. Map fault lines and use to make splits in the terrain and to form mountains.
  4. Apply impact zones, volcano craters, etc.
  5. Simulate water wear.
  6. Map vegetation and factor that into erosion determination similar to the hardness map.
The multi layering should allow the terrain to be generated in such a way as to make it believable. Also, I am exploring which algorithms allow tiling in the math itself. This type of continuous calculation definitely is weighted toward Perlin calculations. The bifurcation algorithms do not lend themselves well to this.

So, onward and upward!

About the author

I love programming, I love programming things that go click, whirr, boom. For organized T3D Links visit: http://demolishun.com/?page_id=67


#1
09/03/2012 (11:03 pm)
Interesting, also great to see this kind of library on Python ?!
i look forward to see the next step ;)
#2
09/03/2012 (11:31 pm)
Yep, it is Python I am using. Right now it is running separate from the engine. I am importing the 16 bit height map via the terrain height map import feature. I have script commands that I have written that can do this as well. I want to leverage Python as much as possible to save coding time. I may even bypass the script command and create the ter file directly. That way I can easily keep this in another process/thread and on a separate CPU. Hence, no perceptible delay while playing a game.

The Perlin noise may make it possible to keep generating terrains as the player moves across the current block. Once the other terrain starts to come in view it can be placed there and remove other terrain blocks. So there is a potential of this allowing for large terrains if the seams are not too bad. I have no idea what this will do to networking or position resolution. That idea is a big maybe. That is not my goal however. I just noticed some websites saying it lends to tileable terrain generation. My goal is realistic places I would choose to take a vacation at if it really existed.

#3
09/04/2012 (12:32 am)
Here is something else I think will make the generated terrain much more convincing:
designfestival.com/the-cicada-principle-and-why-it-matters-to-web-designers/

This is absolutely simple and beautiful.
#4
09/04/2012 (1:46 am)
Hey, nice to see you diving into this Frank. It'll be interesting to see how this project of yours develops. Something I've been thinking of late is the idea of applying a Gaussian blur filter to a greyscale image based on the brightness value of the pixels.

Let's say you have a black image with a white circle in the center, roughly graduated (Gaussian) to produce a falloff effect. Now, you could try applying noise to that where the noise strength is increased as the brightness increases, making whiter areas have more noise. Alternatively you could apply our Gaussian blur to the darker areas, thus creating a nice simulated erosion effect. What I'm thinking is to combine both methods, which I think would produce water flows. I could be wrong though.

The 16 bit heightmap issue is killing me by the way. I'm finding frustration at every turn. World Machine 2, which I use almost exclusively these days for terrain generation, seems to spit everything out in 16bit .bmp files. GIMP no likey. L3DT seems to like it though, so I find I'm forced to use it in many cases. These stepped terrains are just awful to work around, totally ruining most heightmaps.

Anyway, good write up. I'll check in from time to time to track your progress.
#5
09/04/2012 (10:33 am)
I think PythonMagick has Gaussian blur. When I said PythonMagick was missing things it was stuff that allows it to generate data like noise. The function is there, but I cannot use it because the constants are not exported. Very frustrating. I can however modify any image. So I have taken the route of generating the noise data using other functions and then modify/save them with Magick.

One thing I am going to try is the edge detection coupled with the worm creation (can't remember what the functions is called). These can then be used to scar the surface. I think if the worm lines were sufficiently fractal then it could carve rivers.
#6
09/04/2012 (11:42 am)
Hey Frank,

Dont know if youve seen this, however its a good paper and has some great results. http://web.mit.edu/cesium/Public/terrain.pdf

It goes through and shows their calculations used for various erosion methods in heightmap generation.
#7
09/04/2012 (5:32 pm)
@Andy,
No I had not. That looks amazing. Thanks