propellerhat23
propellerhat23

Reputation: 33

how to continuously generate Perlin noise as an infinite map grows?

EDIT: I ended up using the FastNoise library found here: https://github.com/Auburns/FastNoise It has everything under the sun which someone might need to generate many different kinds of noise. As the title might suggest its also quite fast!

I'm creating a 2D infinitely, procedural generated world. I load and unload chunks from disc as the player moves. I'm using a cellular automata function to define how local tiles within each chunk are designed but I need noise (Perlin in this case) to define what biome type each chunk will be as new ones are created. I understand how I would translate decimals between 0 and 1 to represent this, my only issue is that the tutorial I followed on creating Perlin noise requires that you pass it a predefined 2d array and returns a noise array of the same size. Because my world grows dynamically I'm a bit confused on how I would use a fixed sized array to designate new chunk types.

Other answers I've seen don't cover exactly how to handle the infinite part of noise generation. My best guess is that I need to somehow generate or expand the noise with each newly created chunk although how I do this stumps me.

Here is some code I translated to C# from here: http://devmag.org.za/2009/04/25/perlin-noise/

admittedly some of the math here I don't fully understand yet, especially the bitwise function!

public class PerlinNoiseGenerator
    {
        public int OctaveCount { get; set; }
        public float Persistence { get; set; }


        public PerlinNoiseGenerator(int octaveCount, float persistence)
        {
            this.OctaveCount = octaveCount;
            this.Persistence = persistence;
        }

        public float[,] GenerateWhiteNoise(int width, int height)
        {
            float[,] noiseFieldToReturn = new float[width, height];
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    noiseFieldToReturn[i, j] = (float)Game1.Utility.RGenerator.NextDouble() % 1;
                }
            }
            return noiseFieldToReturn;
        }

        public float[,] SmoothNoiseField(float[,] whiteNoise, int octave)
        {
            int width = whiteNoise.GetLength(0);
            int height = whiteNoise.GetLength(1);
            float[,] smoothField = new float[width, height];

            int samplePeriod = 1 << octave;

            float sampleFrequency = 1.0f / samplePeriod;

            for(int i =0; i < width; i++)
            {
                int samplei0 = (i / samplePeriod) * samplePeriod;
                int samplei1 = (samplei0 + samplePeriod) % width;

                float horizontalBlend = (i - samplei0) * sampleFrequency;
                for(int j =0; j < height; j++)
                {
                    int samplej0 = (j/samplePeriod) * samplePeriod;
                    int samplej1 = (samplej0 + samplePeriod) % height;
                    float verticalBlend = (j - samplej0) * sampleFrequency;

                    float top = LinearInterpolate(whiteNoise[samplei0, samplej0],
                        whiteNoise[samplei1, samplej0], horizontalBlend);

                    float bottom = LinearInterpolate(whiteNoise[samplei0, samplej1],
                        whiteNoise[samplei1, samplej1], horizontalBlend);

                    smoothField[i, j] = LinearInterpolate(top, bottom, verticalBlend);
                }
            }

            return smoothField;
        }

        public float[,] GeneratePerlinNoise(float[,] baseNoise, int octaveCount)
        {
            int width = baseNoise.GetLength(0);
            int height = baseNoise.GetLength(1);

            float[][,] smoothNoise = new float[octaveCount][,];

            float persistance = .5f;

            for(int i =0; i < octaveCount;i++)
            {
                smoothNoise[i] = SmoothNoiseField(baseNoise, i);
            }
            float[,] perlinNoise = new float[width, height];
            float amplitude = 1f;
            float totalAmplitude = 0.0f;

            for(int octave = octaveCount - 1; octave > 0; octave-- )
            {
                amplitude *= persistance;
                totalAmplitude += amplitude;

                for(int i =0; i < width;i++)
                {
                    for(int j =0; j < height; j++)
                    {
                        perlinNoise[i, j] += smoothNoise[octave][i, j] * amplitude;
                    }
                }
            }

            for(int i =0; i < width; i++)
            {
                for(int j =0; j < height; j++)
                {
                    perlinNoise[i, j] /= totalAmplitude;
                }
            }
            return perlinNoise;
        }

        public float LinearInterpolate(float a, float b, float alpha)
        {
            return a * (1 - alpha) + alpha * b;
        }
    } 

This code should compile and produces a FIXED size array of noise

Upvotes: 0

Views: 4254

Answers (1)

Koen Rijnsent
Koen Rijnsent

Reputation: 260

The main thing you want to make sure is that the starting random noise is pseudo random, so you always have a "fixed random value" for a coordinate.

It might be that you'd have to rewrite your random noise generator, using the coordinates as input. I imagine your maps have a random seed number, so you could use this post as a starting point, adding 1 factor: A pseudo-random number generator based on 2 inputs

For a bit of inspiration for your map making, I wrote this article a while back: https://steemit.com/map/@beeheap/create-a-fantasy-grid-map-in-excel

Added after your comment: the only function you'd need to change is the GenerateWhiteNoise one. I don't speak C#, but this is the general idea:

GenerateWhiteNoise(int x_start_coord, int y_start_coord, int random_seed) {
    int default_x_width = 100;
    int default_y_heigth = 50;
    float[,] noiseFieldToReturn = new float[width, height];

    for (in x = x_start_coord; i < default_x_width + x_start_coord; x++)
    {
        for (in y = y_start_coord; i < default_y_width + y_start_coord; y++)
        {
            noiseFieldToReturn[i, j] = (float)pseudo_rnd_value(x, y, random_seed);
        }
    }
    return noiseFieldToReturn;
}

That should give you the pseudo random values you need to build your map tiles, the only thing you need is the coordinate of the player (x and y).

Upvotes: 1

Related Questions