Як я можу генерувати плаваючі наземні маси для двигуна, подібного Minecraft?


19

Я створюю двигун подібний Minecraft в XNA. Що я хочу зробити, це створити плаваючі острови, схожі на показані на цьому відео:

http://www.youtube.com/watch?v=gqHVOEPQK5g&feature=related

Як би я повторив це за допомогою світового генератора? Чи повинен я використовувати якийсь алгоритм шуму Perlin? Я не знаю, як це могло б допомогти мені зробити такі земельні масиви.

Ось код для генератора шуму перлін, який я використовую:

    private double[,] noiseValues;
    private float amplitude = 1;    // Max amplitude of the function
    private int frequency = 1;      // Frequency of the function

    /// <summary>
    /// Constructor
    /// </summary>
    /// 
    public PerlinNoise(int freq, float _amp)
    {
        Random rand = new Random(System.Environment.TickCount);
        noiseValues = new double[freq, freq];
        amplitude = _amp;
        frequency = freq;

        // Generate our noise values
        for (int i = 0; i < freq; i++)
        {
            for (int k = 0; k < freq; k++)
            {
                noiseValues[i, k] = rand.NextDouble();
            }
        }
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    public double getInterpolatedPoint(int _xa, int _xb, int _ya, int _yb, double x, double y)
    {
        double i1 = interpolate(
            noiseValues[_xa % Frequency, _ya % frequency],
            noiseValues[_xb % Frequency, _ya % frequency]
            , x);

        double i2 = interpolate(
            noiseValues[_xa % Frequency, _yb % frequency],
            noiseValues[_xb % Frequency, _yb % frequency]
            , x);

        return interpolate(i1, i2, y);
    }

    public static double[,] SumNoiseFunctions(int width, int height, List<PerlinNoise> noiseFunctions)
    {
        double[,] summedValues = new double[width, height];

        // Sum each of the noise functions
        for (int i = 0; i < noiseFunctions.Count; i++)
        {
            double x_step = (float)width / (float)noiseFunctions[i].Frequency;
            double y_step = (float)height / (float)noiseFunctions[i].Frequency;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    int a = (int)(x / x_step);
                    int b = a + 1;
                    int c = (int)(y / y_step);
                    int d = c + 1;

                    double intpl_val = noiseFunctions[i].getInterpolatedPoint(a, b, c, d, (x / x_step) - a, (y / y_step) - c);
                    summedValues[x, y] += intpl_val * noiseFunctions[i].Amplitude;
                }
            }
        }
        return summedValues;
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    private double interpolate(double a, double b, double x)
    {
        double ft = x * Math.PI;
        double f = (1 - Math.Cos(ft)) * .5;

        // Returns a Y value between 0 and 1
        return a * (1 - f) + b * f;
    }

    public float Amplitude { get { return amplitude; } }
    public int Frequency { get { return frequency; } }

Але річ у тому, що автор коду використовує наступне, щоб створювати шум, і я, принаймні, його не розумію.

    private Block[, ,] GenerateLandmass()
    {
        Block[, ,] blocks = new Block[300, 400, 300];

        List<PerlinNoise> perlins = new List<PerlinNoise>();
        perlins.Add(new PerlinNoise(36, 29));
        perlins.Add(new PerlinNoise(4, 33));

        double[,] noisemap = PerlinNoise.SumNoiseFunctions(300, 300, perlins); 

        int centrey = 400 / 2;

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short y = 0; y < blocks.GetLength(1); y++)
            {
                for (short z = 0; z < blocks.GetLength(2); z++)
                {
                    blocks[x, y, z] = new Block(BlockType.none);
                }
            }
        }

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short z = 0; z < blocks.GetLength(2); z++)
            {
                blocks[x, centrey - (int)noisemap[x, z], z].BlockType = BlockType.stone; 
            }
        }

        //blocks = GrowLandmass(blocks);

        return blocks;
    }

І ось сайт, який я використовую: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

І я намагаюся реалізувати шум перліна таким чином, який вказав Мартін Сойка.

Гаразд, ось що я отримав досі:

введіть тут опис зображення

Відповіді:


21

Для базової землі зробіть два 2D безперервних шумових полів (Perlin, Simplex, Wavelet, їх комбінація - все, що працює для вас), одне з переважно низькою частотою. частини з низькою амплітудою для верхньої межі суші, інші з високою частотою, з високою амплітудою і низькою частотою, з високою амплітудою для нижньої межі суші. Якщо нижня межа вище вищої межі, не включайте наземні вокселі (або те, що ваша гра використовуватиме для представлення місцевості). Кінцевий результат виглядає приблизно так ...


Але це для 2D чи не так?
Дарестіум

Але мені це дуже подобається :)
Darestium

4
2D / 3D - те саме
Гевін Вільямс

Гаразд,
невдала

@Darestium: це двомірний приклад для легшої візуалізації. Цей же метод працює для будь-якої кількості (алгебраїчних) розмірів, вищих за один.
Мартін Сойка

15

Чи вистачило б чогось подібного?

введіть тут опис зображення

Якщо так, перевірте цю статтю . Цитуючи найбільш релевантні частини:

Для отримання більш цікавого шуму можна додати разом кілька октав симплексного шуму. [...] Оскільки я хочу отримати приблизно сферичну плаваючу скелю сортів, мені потрібно помножити шум на його відстань від центру. [...] Я також хочу, щоб скеля була вгорі більш плоскою, ніж знизу, отже, другий коефіцієнт множення є градієнтом у напрямку у. Поєднуючи їх разом і розтягуючи y для шуму, стискаючи x і za bit, ми отримуємо щось на зразок плаваючої скелі. [...] Розкопки печер з іншим випадком компенсації шуму також роблять його цікавішим.

  • Тому в основному ви почнете з набору даних, що генерується з шуму симплексного чи перлінського режиму (а точніше з кількох октав шуму, що додаються разом ).
  • Потім сформуйте його в щось ближче до плаваючої суші, зробивши її більш кулястою (помноживши шум на відстань від центру ).
  • І створіть землю, зробивши її більш плоскою біля верху (помноживши її на вертикальний градієнт, тобто починаючи з низьких значень у верхній частині та піднімаючись вище до нижнього).
  • Поєднайте ці три та відрегулюйте форму, масштабуючи шум уздовж осей X / Y / Z (стаття пропонує натягувати на вісь Y і стискати на осі X і Z ).
  • Для розкопок печер може використовуватися додатковий прохід шуму .

Так, я вважаю, що щось подібне викликає те, що я хочу. Вся справа в тому, що у мене мало досвіду з шумом перлин, тому єдине, що я можу генерувати - це справді основні гори, і я б не мав жодних уявлень про те, як додати "декілька октав перемикача шуму". Для генерації шуму перлін я використовую код що я зняв stackoverflow.com/questions/4753055/… і переніс його на C #. Додаю мою версію в оригінальному дописі ... Чи готові ви надати мені приклад того, як я досяг би такої земельної маси з цим код?
Дарестіум

2
Ось чому я пов’язав статтю. У ньому є пояснення всіх етапів та вихідний код в кінці. Вам слід спробувати це вивчити.
Девід Гувейя

4
  1. Використовуючи наявну 3D-сітку, визначтеся, на якій висоті ви хочете розташовувати острівні вершини. Створіть набір островів у цій двовимірній площині (давайте назвемо її площиною XY), розсипаючи точки по площині, а потім розміщуючи кубики в цих точках. Використовуйте згуртованість, щоб зблизити їх згустками. Заповніть будь-які отвори і у вас є набір островів.
  2. Використовуйте ЦА-подобний метод вирощування островів вниз. (a) Починаючи з рівня Z, де ви побудували свої початкові точки, для кожної комірки в цьому поточному рівні Z визначте шанс поширитись до наступного нижнього рівня, враховуючи кількість сусідів у площині XY, від 0 до 8 ( діагональні сусіди включені), наприклад, призначте 10% шансів для кожного сусіда, максимум до 80% шансу. Обчисліть це для кожної комірки в початковій площині. (b) Потім рандомізуйте цей шанс і продовжте вниз, якщо ви знаходитесь в межах відсотка. Промийте, повторіть крок 2 (перейдіть до наступного рівня, визначте сусідів для кожного вокселя, продовжте вниз для цього вокселя), поки більше не відбудеться розширення. Ваше розширення вниз має утворювати конус завдяки підходу кількості сусідів, тому що в цих вокселях до XY-центру острова зазвичай буде більше сусідів.

Псевдокод для кроку 2:

int planeNeighbours[x][y]; //stores how many neighbours each voxel in this plane has

for each z level (starting at level where you plotted your points)
    for each x, y voxel in z level
        for each neighbour space bordering this voxel
            if neighbour exists
                ++planeNeighbours[x][y];
    for each x, y voxel in z level
        chance = random(0,8); //depends on your RNG implementation
        if chance < planeNeighbours[x][y]
            worldGrid[x][y][z+1] = new cube

Після того, як ваші острови будуть створені, ви можете додатково переміщувати їх вгору та вниз у просторі, щоб мати їх на різній висоті.


Гаразд, у мене був тріщина у вашому методі, і, здається, зростає місцевість назовні, а не всередину. Я пошлю код ...
Darestium
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.