Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Шум Перлина, подробная характеристика и реализация ландшафтов на его основе

Читайте также:
  1. I. ОБЩАЯ ХАРАКТЕРИСТИКА КУРСА
  2. I. Общие требования к характеристикам лифтов и устройств безопасности лифтов
  3. I. Характеристика посещенных объектов космодрома Байконур
  4. II. Общая характеристика учебного предмета
  5. II. Правовая характеристика злоупотребления правом.
  6. II. Специальные требования к характеристикам лифтов и устройств безопасности лифтов, предназначенных в том числе для инвалидов и других маломобильных групп населения
  7. III. Характеристика обобщенных трудовых функций

Шум Перлина(Perlin noise) – математический алгоритм для генерирования градиентного шума. Шум был создан Кеном Перлином в 1983 году, назван в честь своего создателя.

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

Благодаря масштабируемости алгоритма в одно-, двух- и трехмерного видов, он используется для генерации: текстур, карт высот, воксельных структур. Так же он используется для создания таких визуальных эффектов как: дым, облака, туман, огонь и другие.

Наличие множества входных параметров для функции Перлина делает ее шум гибко регулированным. Основные параметры функции: амплитуда(amplitude) - максимальное значение шума; частота(frequency) – величина, численно равная 1/wavelength(длинна волны – длинна между концом возрастающего участка функции и началом спадающего(и наоборот)), стойкость(persistence) – величина связывающая амплитуду и частоту в один параметр.

Для создания более качественного ландшафта и увеличения влияния параметров функции на конечный результат, в функцию реализующюю шум Перлина добавляют октавы. Октавы – каждая выполненная функция шума, выполненная с удвоенным значением частоты прошлой октавы.
Одной из важных частей функции шума является интерполяция между значениями соседних с данным узлом узлов. Для этого могут быть использованы такие виды интерполяции: линейная, косинусная и кубическая. Линейная функция – наиболее быстрая, но при этом выдает результаты намного хуже остальных. Косинусная и кубическая функции интерполяции – выдают очень плавный результат, но за это мы платим в скорости. Что быстрее/красивее – косинусная или кубическая – предмет для спора, так как и скорость и плавность у них примерно одинаковая, но в теории кубическая должна быть эффективней, за счет более высокого качества интерполяции и отсутствия ресурсоемких функций, таких как cos.

Реализации разных типов интерполяции на C++:

Float linear_mix(float a, float b, float k){

Return a + (b - a)* k;

}

float cos_mix(float a, float b, float x){

float f = (float)((1.0 - cosf(x * Pi)) / 2.0);

return (float)(a * (1.0 - f) + b * f);

}

Сама реализация шума Перлина на C++ выглядит так:

float PerlinNoise(float x, float y, float seed_x = 660, float seed_y = 660){

float total = 0.0;

float pres = (float)0.01; //любое число

float ampl = (float)100.2; //любое число

float freq = (float)0.06; //любое число

int i;

for (i = 0; i < num_octaves; i++)

{

total += final_noise(x * freq + seed_x, y * freq + seed_y) * ampl;

ampl *= pres;

freq *= 2;

}

return total;

}

Для генерации ландшафта с помощью шумов Перлина делаются такие шаги:

1) Инициализация двухмерного массива

2) Для каждого узла в массиве вычислить значение шумом Перлина, посылая параметрами функции x и y индексы текущего узла * v(2D вектор; x, y!= 0)

Heightmap Generate(int max_x, int max_y, int seed = 0){

int i = 0;

int j = 0;

vector<vector<float>> _data(max_x);

for (i = 0; i < (max_x); i++)

{

_data[i].resize(max_y);

for (j = 0; j < (max_y); j++)

{

_data[i][j] = (float)(PerlinNoise((float)i * offset_coef_x, (float)j * offset_coef_y, (float)seed, (float)seed) - 10.0);

}

}

return Heightmap(_data);

}

3) Триангулировать карту высот путем деления каждых 4 близ лежащих узлов на треугольникb и вычислить вспомогательные данные для рендеринга

static TriangleData Generate(Heightmap heightmap){

int size = heightmap.XBounds * heightmap.YBounds * VERTEX_NUMBERS_PER_TRIANGLE;

float *vertexes = new float[size];

size = heightmap.XBounds * heightmap.YBounds * TEX_COORDS_NUMBERS_PER_TRIANGLE;

float *texcoords = new float[size];

int i = 0;

int j = 0;

int g = 0;

int c = 0;

float maxf = (float)heightmap.XBounds;

for (i = 0; i < heightmap.XBounds - 1; i++)

for (j = 0; j < heightmap.YBounds - 1; j++)

{

vertexes[c] = (float)i + (float)heightmap.XOffset;

vertexes[c + 1] = heightmap.Data[i][j];

vertexes[c + 2] = (float)j + (float)heightmap.YOffset;

texcoords[g] = (float)i / maxf;

texcoords[g + 1] = (float)j / maxf;

vertexes[c + 3] = (float)(i + 1) + (float)heightmap.XOffset;

vertexes[c + 4] = heightmap.Data[i + 1][j + 1];

vertexes[c + 5] = (float)(j + 1) + (float)heightmap.YOffset;

texcoords[g + 2] = (float)(i + 1) / maxf;

texcoords[g + 3] = (float)(j + 1) / maxf;

vertexes[c + 6] = (float)(i + 1) + (float)heightmap.XOffset;

vertexes[c + 7] = heightmap.Data[i + 1][j];

vertexes[c + 8] = (float)j + (float)heightmap.YOffset;

texcoords[g + 4] = (float)(i + 1) / maxf;

texcoords[g + 5] = (float)j / maxf;

vertexes[c + 9] = (float)i + (float)heightmap.XOffset;

vertexes[c + 10] = heightmap.Data[i][j];

vertexes[c + 11] = (float)j + (float)heightmap.YOffset;

texcoords[g + 6] = (float)i / maxf;

texcoords[g + 7] = (float)j / maxf;

vertexes[c + 12] = (float)(i + 1) + (float)heightmap.XOffset;

vertexes[c + 13] = heightmap.Data[i + 1][j + 1];

vertexes[c + 14] = (float)(j + 1) + (float)heightmap.YOffset;

texcoords[g + 8] = (float)(i + 1) / maxf;

texcoords[g + 9] = (float)(j + 1) / maxf;

vertexes[c + 15] = (float)i + (float)heightmap.XOffset;

vertexes[c + 16] = heightmap.Data[i][j + 1];

vertexes[c + 17] = (float)(j + 1) + (float)heightmap.YOffset;

texcoords[g + 10] = (float)i / maxf;

texcoords[g + 11] = (float)(j + 1) / maxf;

c += 18;

g += 12;

}

return TriangleData(vertexes, texcoords, NULL, heightmap.XBounds * heightmap.YBounds * 2, heightmap.XBounds, heightmap.YBounds);

}

4) Отрендерить ландшафт, используя полученные вершины.

Несмотря на большое количество параметров в функции, ее шум не создает завершенный ландшафт. Он выглядит просто как кусок ландшафта. Для этого я решил разработать функцию пост обработки карты высот сгенерированной по любому алгоритму, которая давала бы на выходе остров. Найденый мной алгоритм анализирует к какой границе ближе данный узел(обязательно принадлежащий области waterside), определяет расстояние(далее l) до нее и на основе этих данных возвращает итоговую высоту узла. Базовая функция выглядит так:

f(h, x, y) = h - h * (waterside - min(x, y)) / waterside

Реализация алгоритма на C++:

static Heightmap IslandFilter(Heightmap _heightmap, int _underwater_size = 100)

{

vector<vector<float>> island_height_map(_heightmap.XBounds);

int i = 0;

int j = 0;

int max_x = _heightmap.XBounds;

float max_xf = (float)max_x;

int max_y = _heightmap.YBounds;

float max_yf = (float)max_y;

int max = max_x;

float maxf = (float)max;

int underwater_size = _underwater_size;

float underwater_sizef = (float)underwater_size;

int far_uw_side = max - underwater_size;

float far_uw_sidef = (float)far_uw_side;

for (i = 0; i < max_x; i++)

{

island_height_map[i].resize(max_y);

for (j = 0; j < max_y; j++)

{

bool done = false;

island_height_map[i][j] = _heightmap.Data[i][j];

if (i < underwater_size && j < underwater_size)

{

if (i <= j)

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - i) / underwater_sizef);

done = true;

}

if (j < i)

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - j) / underwater_sizef);

done = true;

}

}

if (i < underwater_size && j > far_uw_sidef)

if (i > -(j - max_yf))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - j) / underwater_sizef);

done = true;

}

else

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - i) / underwater_sizef);

done = true;

}

if (j < underwater_size && i > far_uw_side)

if (j > -(i - max_xf))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - i) / underwater_sizef);

done = true;

}

else

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - j) / underwater_sizef);

done = true;

}

if (i < underwater_size && (j >= underwater_size && j <= far_uw_side))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - i) / underwater_sizef);

done = true;

}

if (j < underwater_size && (i >= underwater_size && i <= far_uw_side))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * ((underwater_sizef - j) / underwater_sizef);

done = true;

}

if (i > far_uw_side && (j >= underwater_size && j <= far_uw_side))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - i) / underwater_sizef);

done = true;

}

if (j > far_uw_side && (i >= underwater_size && i <= far_uw_side))

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - j) / underwater_sizef);

done = true;

}

if (!done)

{

if (i > far_uw_side && j > far_uw_side)

{

if (i >= j)

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - i) / underwater_sizef);

done = true;

}

if (j > i)

{

island_height_map[i][j] -= _heightmap.Data[i][j] * (-(far_uw_sidef - j) / underwater_sizef);

done = true;

}

}

}

}

}

return Heightmap(island_height_map);

}

 

 

Программа

Программа “Landscape Advanced Gen” написана на C++ и является графическим редакторов ландшафтов. Для ее написания было использовано всего одна сторонняя библиотека: OpenIL(DevIL) – библиотека для работы с текстурами. В качестве графического API программа использует OpenGL2.0 и расширения 3.3+. Для рендеринга ландшафта использовалась технология VBO. На протяжении разработки программы мною была написана небольшая графическая оболочка над OpenGL.

Визуальные эффекты реализованные в программе на данный момент:

Все(кроме мильтисемплинга) эффекты были реализованы с помощью шейдеров.

Также реализована функция генерации абсолютно случайного ландшафта(путем задавания постоянного(на протяжении заполнения карты высот) рандомного offset параметра).

Программа имеет средства редактирования. При наведении мышкой на ландшафт выделяется область, которую при нажатии на левую/правую кнопку мыши можно поднимать/опускать.

При нажатии на клавишу “shift” выскакивает панель-меню, в котором есть кнопки “save” и “load”. Кнопка “save” сохраняет текущий ландшафт в файл “landscape.bhef”. Кнопка “load” загружает последний сохраненный ландшафт.

Формат “BHEF” - бинарный созданный мной специально для ландшафтов. В нем хранится только карта высот. Функция записи:

 

static void exportBin(Heightmap _heightmap){

std::ofstream file("landscape.bhef");

FILE *f = new FILE();

f = fopen("landscape.bhef", "wb");

int *HM_global_header = new int[2];

HM_global_header[0] = _heightmap.XBounds;

HM_global_header[1] = _heightmap.YBounds;

fwrite(HM_global_header, sizeof(int), 2, f);

int *nodeID = new int[2];

float *nodeHeight = new float(0);

for (int i = 0; i < _heightmap.XBounds; i++)

for (int j = 0; j < _heightmap.YBounds; j++)

{

nodeID[0] = i;

nodeID[1] = j;

nodeHeight[0] = _heightmap.Data[i][j];

fwrite(nodeID, sizeof(int), 2, f);

fwrite(nodeHeight, sizeof(float), 1, f);

}

delete HM_global_header;

delete [] nodeID;

delete nodeHeight;

fclose(f); }

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

 


Дата добавления: 2015-11-04; просмотров: 495 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Способы описания ландшафтных структур| Голова и ее внутренности

mybiblioteka.su - 2015-2024 год. (0.039 сек.)