Как непрерывно генерировать шум Перлина по мере роста бесконечной карты? - PullRequest
0 голосов
/ 04 октября 2019

Я создаю 2D бесконечно, процедурно сгенерированный мир. Я загружаю и выгружаю куски с диска, когда проигрыватель движется. Я использую функцию клеточного автомата, чтобы определить, как создаются локальные тайлы в каждом чанке, но мне нужен шум (в данном случае Perlin), чтобы определить, какой тип биома будет у каждого чанка при создании новых. Я понимаю, как я бы перевел десятичные числа между 0 и 1, чтобы представить это, моя единственная проблема в том, что учебник, которому я следовал при создании шума Перлина, требует, чтобы вы передали ему предопределенный 2d массив и возвращали массив шума того же размера. Поскольку мой мир динамично развивается, я немного запутался в том, как использовать массив фиксированного размера для обозначения новых типов чанков.

Другие ответы, которые я видел, не охватывают точно, как обращаться с бесконечной частью генерации шума. Мое лучшее предположение заключается в том, что мне нужно каким-то образом генерировать или расширять шум с каждым вновь созданным фрагментом, хотя то, как я это делаю, ставит меня в тупик.

Вот код, который я перевел на C # отсюда: http://devmag.org.za/2009/04/25/perlin-noise/

по общему признанию, часть математики, которую я здесь до сих пор не полностью понял, особенно побитовая функция!

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;
        }
    } 

Этот код должен скомпилироваться и создать массив шумов фиксированного размера

1 Ответ

0 голосов
/ 15 октября 2019

Главное, что вы хотите убедиться в том, что начальный случайный шум является псевдослучайным, поэтому у вас всегда есть «фиксированное случайное значение» для координаты.

Возможно, вам придетсяпереписать ваш генератор случайных шумов, используя координаты в качестве входных данных. Я предполагаю, что ваши карты имеют случайное начальное число, поэтому вы можете использовать этот пост в качестве отправной точки, добавив 1 фактор: Генератор псевдослучайных чисел, основанный на 2 входах

Для немногоВдохновившись созданием вашей карты, я написал эту статью некоторое время назад: https://steemit.com/map/@beeheap/create-a-fantasy-grid-map-in-excel

Добавлено после вашего комментария: единственная функция, которую вам нужно изменить, - это функция GenerateWhiteNoise. Я не говорю на C #, но это общая идея:

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;
}

Это должно дать вам псевдослучайные значения, которые вам нужны для построения тайлов вашей карты, единственное, что вам нужно, это координата игрока(х и у).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...