【问题标题】:how to continuously generate Perlin noise as an infinite map grows?随着无限图的增长,如何不断产生 Perlin 噪声?
【发布时间】:2019-10-04 18:07:32
【问题描述】:

编辑:我最终使用了在这里找到的 FastNoise 库:https://github.com/Auburns/FastNoise 它拥有阳光下的所有东西,有人可能需要这些东西来产生许多不同种类的噪音。正如标题所暗示的那样,它也很快!

我正在创建一个由程序生成的无限二维世界。当播放器移动时,我从光盘加载和卸载块。我正在使用元胞自动机函数来定义如何设计每个块中的局部图块,但我需要噪声(在本例中为 Perlin)来定义每个块在创建新块时将是什么生物群系类型。我了解如何转换 0 和 1 之间的小数来表示这一点,我唯一的问题是我遵循的关于创建 Perlin 噪声的教程要求您将一个预定义的二维数组传递给它并返回一个相同大小的噪声数组。因为我的世界是动态增长的,所以我对如何使用固定大小的数组来指定新的块类型有点困惑。

我看到的其他答案并未准确涵盖如何处理噪声生成的无限部分。我最好的猜测是,我需要以某种方式生成或扩展每个新创建的块的噪音,尽管我是如何做到这一点的。

这是我从这里翻译成 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;
        }
    } 

这段代码应该编译并产生一个固定大小的噪声数组

【问题讨论】:

  • 您可能正在寻找类似:forum.unity.com/threads/… 这一切都归结为使用perlinNoise[i, j] and width, height。我没有时间为您提供更多帮助。

标签: c# xna monogame procedural-generation


【解决方案1】:

您要确保的主要内容是起始随机噪声是伪随机的,因此您始终有一个坐标的“固定随机值”。

您可能需要重写随机噪声发生器,使用坐标作为输入。我想你的地图有一个随机的种子数,所以你可以使用这篇文章作为起点,添加 1 个因素: A pseudo-random number generator based on 2 inputs

为了给您的地图制作带来一点灵感,我不久前写了这篇文章: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;
}

这应该为您提供构建地图图块所需的伪随机值,您唯一需要的是玩家的坐标(x 和 y)。

【讨论】:

  • 感谢您的回复。您的 Excel 地图非常酷。但是,我不确定如何解释您链接的问题。我知道我需要使用坐标作为输入,但我不确定如果不以某种方式引用一个设置的二维数组(就像你在你的数组中一样)我会如何做到这一点。
  • 用一些样机代码更新了我的答案,有帮助吗?
猜你喜欢
  • 2012-03-08
  • 1970-01-01
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
相关资源
最近更新 更多