Создание Ландшафта. Фокусирование на создании Ландщафта (Перевод) Часть 6

Вы вероятно обратили внимание, что я добавил простой класс RandomHelper, чтобы избежать перезаписывания того же самого кода всюду в проекте.

В классе GameFOT я только что добавил несколько линий, чтобы отобразить текущий алгоритм, используемый для ландшафта. Вы можете нажать B или Enter, чтобы перейти от одного heightmap до другого.

Вы можете найти исходный текст в папке P301FaultGeneration.

Смещение в средней точке

Такие горные цепи, как Гималаи сформированы геологическим процессом, названным подъемом. Боковое давление от движения тектонических пластин заставляет поверхность Земли морщиться, поднимая горные цепи. Мы можем моделировать этот эффект, используя алгоритм смещения в средней точке, также известного как плазменный фрактал или алмазный квадратный алгоритм.

На плоскости смещение в средней точке работает так. Возьмите линейный сегмент, выберите среднюю точку и измените его случайным значением в маленьком диапазоне (-половина длины, +половина длины сегмента). После этого Вы уменьшаете диапазон, и вы повторяете процесс для каждого нового сегмента.

В нашем алгоритме, при каждой итерации, диапазон смещения умножен на 2 ^-r, где r — константа грубости.

Когда r> 1, смещение будет уменьшаться быстрее чем длина линейного сегмента, таким образом ландшафт будет похож на гору или точку минимума, очень гладкую.

Когда r = 1, это лучший результат, маленькие области будут похожи на большие области.

Когда r <1, мы заканчиваем с очень хаотическим результатом. Последние итерации имеют непропорционально большой эффект на ландшафт.

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

Чтобы сделать то, что нам надо, мы начинаем с прямоугольника ABCD, со значениями высоты в четырех углах. Мы вычисляем высоту в середине E, усредняя высоты вершин A, B, C и D, и добавляем случайное значение в определенном диапазоне (в нашем случае, половина длины ширины). Это — алмазный шаг. Теперь квадратный шаг — вычисляем высоты в середине линейных сегментов, усредняя значения вершин и середины смежных прямоугольников, и снова добавляем случайное значение в определенном диапазоне. Вы можете повторить процесс столько раз, сколько вы хотите.

Так как квадратный шаг полагается на алмазные значения соседнего квадрата, мы должны сначала выполнить каждый алмазный шаг прежде, чем сделать квадратный шаг.

Вот некоторые скриншоты, чтобы иллюстрировать смещение в средней точке:


Алмазный шаг только с r = 1


Алмазный шаг только с r = 0.25


Алмазный шаг с r = 4.0


Смещение с r = 1.0


Смещение с r = 1.0 в каркасе.


Смещение с r = 0.25


Смещение с r = 4.0

Очевидно, что когда r близко к 1.0, мы получаем наилучшие результаты.

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

Вот суть heightmap класса, в котором есть смещение в средней точке:

/// <summary>

/// Generate a random heightmap using mid point displacement and passed parameters.

/// </summary>

/// <param name=»width»></param>

/// <param name=»depth»></param>

/// <param name=»min»></param>

/// <param name=»max»></param>

/// <param name=»settings»></param>

public void GenerateMidPointHeightmap(int width,

int depth,

float min,

float max,

HeightmapMidPointSettings settings)

{

_width = width;

_depth = depth;

_midPointSettings = settings;

_heightValues = new float[_width * _depth];

GenerateMidPointHeightmap();

}

/// <summary>

/// Generate a random heightmap using mid point displacement and default parameters.

/// </summary>

public void GenerateMidPointHeightmap()

{

int i, ni, mi, pmi;

int j, nj, mj, pmj;

int width = _width;

int depth = _depth;

float deltaHeight = (float)width * 0.5f;

float r = (float)Math.Pow(2.0f, -1 * _midPointSettings.Rough);

// Since the terrain wraps, all four corners are represented by the value at 0, 0.

// So seeding the heightfield is very straightforward.

_heightValues[0] = 1337.0f;

while(width > 0)

{

// Diamond step.

//

// We find the values at the center of the rectangles by averaging the values at the

// corners and adding a random offset.

//

// a . . . b

// . .

// . e . e = ( a + b + c + d ) / 4 + random

// . .

// c . . . d

//

// a = (i, j)

// b = (ni, j)

// c = (i, nj)

// d = (ni, nj)

// e = (mi, mj)

for (i = 0; i < _width; i += width)

{

for (j = 0; j < _depth; j += depth)

{

ni = (i + width) % _width;

nj = (j + depth) % _depth;

mi = (i + width / 2);

mj = (j + depth / 2);

_heightValues[mi + _width * mj] =

(_heightValues[i + j * _width] +

_heightValues[ni + j * _width] +

_heightValues[i + nj * _width] +

_heightValues[ni + nj * _width])

* 0.25f + RandomHelper.GetFloatInRange(-deltaHeight * 0.5f,

deltaHeight * 0.5f);

}

}

// Square step.

//

// We find the values on the left and top sides of each rectangle.

// The right and bottom sides are the left and top sides of the neighboring rectangles.

// So we don’t need to calculate them.

//

// Since the heightmap wraps, we are never left hanging.

// The right side of the last rectangle

// in a row is the left side of the first rectangle in the row.

// The bottom side of the last rectangle

// in a column is the top side of the first rectangle in the column.

//

// a . . . b

// . . . . .

// . . . . .

// . . . . .

// c . . . d

//

// . . . . .

// . . . . .

// . . e . .

// . . . . .

// . . . . .

//

// a . f . b

// . . . . .

// g . e . h

// . . . . .

// c . i . d

//

// a . f . b

// . j l k . g = ( d + f + a + b ) / 4 + random

// g . e . h h = ( a + c + e + f ) / 4 + random

// . . . . .

// c . i . d

//

// a = (i, j)

// b = (ni, j)

// c = (i, nj)

// d = (mi, pmj)

// e = (pmi, mj)

// f = (mi, mj)

// g = (mi, j)

// h = (i, mj)

for (i = 0; i < _width; i += width)

{

for (j = 0; j < _depth; j += depth)

{

ni = (i + width) % _width;

nj = (j + depth) % _depth;

mi = (i + width / 2);

mj = (j + depth / 2);

pmi = (i — width / 2 + _width) % _width;

pmj = (j — depth / 2 + _depth) % _depth;

// Calculate the square value for the top side of the rectangle.

_heightValues[mi + j * _width] =

(_heightValues[i + j * _width] +

_heightValues[ni + j * _width] +

_heightValues[mi + pmj * _width] +

_heightValues[mi + mj * _width]) * 0.25f +

RandomHelper.GetFloatInRange(-deltaHeight * 0.5f,

deltaHeight * 0.5f);

// Calculate the square value for the left side of the rectangle.

_heightValues[i + mj * _width] =

(_heightValues[i + j * _width] +

_heightValues[i + nj * _width] +

_heightValues[pmi + mj * _width] +

_heightValues[mi + mj * _width]) * 0.25f +

RandomHelper.GetFloatInRange(-deltaHeight * 0.5f,

deltaHeight * 0.5f);

}

}

// Set the values for the next iteration.

width /= 2;

depth /= 2;

deltaHeight *= r;

}

// Normalize heightmap (height field values in the range _minimumHeight — _maximumHeight.

NormalizeHeightmap();

}

Вы можете просто нажать кнопку B или клавишу ENTER, чтобы циклически пройти все типы heightmap.

Вы можете найти исходный текст в папке P302MidPointDisplacement.

Смещение частицы

В природе вулканические горные цепи и системы острова как Тихоокеанский регион созданы потоком лавы. В этой части я использую систему частиц, заимствованную от поля молекулярной эпитаксии луча, чтобы моделировать поток лавы. Оригинальная идея исходит от Jason Shankel.

Идея этого алгоритма состоит в том, чтобы пропустить последовательности частиц и моделировать их поток через поверхность, составленную из ранее пропущенных частиц. Пропуск достаточного числа частиц создаст структуры, которые похожи на шаблоны потока вязкой жидкости (как лава). Мы начинаем с пустого heightmap, и мы пропускаем одну частицу в случайном положении. После этого мы пропускаем вторую частицу на вершине первой, и «встряхиваем» heightmap, пока они не подойдут к концу (пока ни один из его соседей не находится ниже). Мы продолжаем пропускать частицы, пока у нас их не будет достаточно. Если мы сохраним пункт снижения в единственном месте, то у нас будет большой пик, перемещая точку снижения, мы периодически будем создавать цепочки множественных маленьких пиков.

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

Вот некоторые иллюстрации:


Кальдера = 0.7, 1024 перехода, peak walk = 4, минимальный пропуск частиц = 128, максимальный — 1024.


То же самое как выше в каркасе.


Кальдера = 0.5, 1024 перехода, peak walk = 4, минимальный пропуск частиц = 128, максимальный — 1024.


То же самое в каркасе.


Кальдера = 0.7, 1024 перехода, peak walk = 8, минимальный пропуск частиц = 128, максимальный — 1024.


То же самое в каркасе.


Кальдера = 0.5, 128 переходов, peak walk = 2, минимальный пропуск частиц = 128, максимальный — 1024.


Кальдера = 0.5, 1024 перехода, peak walk = 2, минимальный пропуск частиц = 128, максимальный — 1024.

Вы можете получить все виды результатов с этим алгоритмом. Выглядит еще лучше, когда вы работаете с водой.

Как прежде, мы создаем класс HeightmapParticleDepositionSettings, чтобы хранить параметры настройки. У нас есть высота кальдеры, число переходов, число пикового значения обхода, минимума и максимума пропуска частиц.

Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s