Zelimir Fedoran
email zelimir.fedoran@gmail.com

Procedural Terrain!

Posted on
May 19th, 2010
Tags
, ,

Finally, I have a terrain generator and the results look a lot better than I had anticipated. It generates various terrain styles by using height-maps which are procedurally generated using a simple perlin noise function. By simply changing a few constants, I can create all sorts of awesome terrains.

01234567

Perlin Noise Function

A perlin noise function is simply a continuous noise function which always generates the same number given the same input.

Wikipedia:
Perlin noise is implemented as a function of either (x,y,z) or (x,y,z,time) which uses interpolation between a set of pre-calculated gradient vectors to construct a value that varies pseudo-randomly over space and/or time.

Perlin noise is widely used in computer graphics for visual effects like fire, smoke, and clouds. It is also frequently used to generate textures when memory is extremely limited, such as in demos, and is increasingly finding use in Graphics Processing Units for real-time graphics in computer games.

I spent some time researching various different perlin noise functions before creating the terrain generator. A great guide to creating perlin noise functions can be found here. The guide details much of the inner workings of my code below.

        // 2d perlin noise function
        public float[,] perlinnoise(int seed, int width, int height, int octaves, float persistence, int zoom)
        {
            float x, y;
            int floorx, floory;
            float na, nb, nc, nd;
            float la, lb, lc;
            float frequency, amplitude;

            float[,] density = new float[width, height];

            for (int j = 0; j < height; j++)
            {
                for (int i = 0; i < width; i++)
                {
                    for (int o = 0; o <= octaves; o++)
                    {
                        frequency = (float)Math.Pow(2, o);
                        amplitude = (float)Math.Pow(persistence, o);

                        x = ((float)i) * frequency / zoom;
                        y = ((float)j) / zoom * frequency;
                        floorx = (int)x;
                        floory = (int)y;
                        na = random(seed, floorx, floory, 0);
                        nb = random(seed, floorx + 1, floory, 0);
                        nc = random(seed, floorx, floory + 1, 0);
                        nd = random(seed, floorx + 1, floory + 1, 0);
                        la = lerp(na, nb, x - floorx);
                        lb = lerp(nc, nd, x - floorx);
                        lc = lerp(la, lb, y - floory);

                        density[i, j] += lc * amplitude;
                    }
                }
            }

            return density;
        }

        // "random" number between -1.0f and 1.0f
        public float random(int seed, int x, int y, int z)
        {
            int n = seed + x*73 + y*179 + z*283;
            n = (n << 13) ^ n;
            int m = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
            return 1.0f - ((float)m / 1073741824.0f);
        }

        // cosine interpolation between a and b by a factor of x
        public float lerp(float a, float b, float x)
        {
            float ft = x * 3.1415927f;
            float f = (1.0f - (float)Math.Cos(ft)) * 0.5f;
            return a * (1.0f - f) + b * f;
        }