Zelimir Fedoran
email zelimir.fedoran@gmail.com

Procedural Caves!

Posted on
June 2nd, 2010
Tags
, ,

I made some modifications to the previous terrain generator to allow it to generate cave like features. This is done by combining the 2d and 3d perlin noise density functions. The great thing about the 3d noise function is that it can be used to create mineral deposits. However, the 3d noise function is much slower than its 2d counterpart. Thus, I may look into alternative ways of generating similar features.

01234567

3D Perlin Noise Function

Once again I have decided to include the source code for the noise function. Because the function is much more computationally expensive I cheated a little by storing the random numbers in a small array. Since the voxels are large, they do not show a lot of fine grain details. Therefore, I can get away with using a small array of random numbers without any noticeable tiling.

Note: For more information about perlin noise functions see my previous post on procedural terrain.

        // 3d perlin noise function
        public float[,,] perlinnoise(int seed, int width, int height, int depth, int octaves, float persistence, int zoom)
        {
            int size = 8; // the size of the noise volume
            int floorx, floory, floorz;
            float x, y, z;
            float na, nb, nc, nd, ne, nf, ng, nh;
            float la, lb, lc, ld, le, lf;
            int ixl, ixh, iyl, iyh, izl, izh;
            float frequency, amplitude;
            float[, ,] density = new float[width, height, depth];
            float[, ,] noisevolume = new float[size, size, size];

            for (int j = 0; j < size; j++)
            {
                for (int k = 0; k < size; k++)
                {
                    for (int i = 0; i < size; i++)
                    {
                        noisevolume[i, j, k] = random(seed, i, j, k);
                    }
                }
            }

            for (int j = 0; j < height; j++)
            {
                for (int k = 0; k < depth; k++)
                {
                    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;
                            z = ((float)j) / zoom * frequency;
                            y = ((float)k) * frequency / zoom;

                            floorx = (int)x;
                            floory = (int)y;
                            floorz = (int)z;

                            ixl = (floorx + 0) % size;
                            ixh = (floorx + 1) % size;
                            iyl = (floory + 0) % size;
                            iyh = (floory + 1) % size;
                            izl = (floorz + 0) % size;
                            izh = (floorz + 1) % size;

                            na = noisevolume[ixl, iyl, izl];
                            nb = noisevolume[ixh, iyl, izl];
                            nc = noisevolume[ixl, iyh, izl];
                            nd = noisevolume[ixh, iyh, izl];
                            ne = noisevolume[ixl, iyl, izh];
                            nf = noisevolume[ixh, iyl, izh];
                            ng = noisevolume[ixl, iyh, izh];
                            nh = noisevolume[ixh, iyh, izh];

                            la = lerp(na, nb, x - floorx);
                            lb = lerp(nc, nd, x - floorx);
                            lc = lerp(ne, nf, x - floorx);
                            ld = lerp(ng, nh, x - floorx);
                            le = lerp(la, lb, y - floory);
                            lf = lerp(lc, ld, y - floory);

                            density[i, j, k] += lerp(le, lf, z - floorz) * 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;
        }