Алгоритм генерации ландшафта Diamond-square

Автор: Денис Обозинцев
 Diamond-square (или square-diamond), расширение одномерного алгоритма midpoint displacement на двумерную плоскость.
 Начнем с более простого алгоритма midpoint displacement. Как уже сказано, он работает не на двумерной плоскости, а на одномерном отрезке (поэтому с его помощью можно, например, создать линию горизонта).
 То, что роднит этот алгоритм с фракталами — это его рекурсивное поведение. Изначально мы любым образом задаем высоту на концах отрезка и разбиваем его точкой посередине на два под-отрезка. Эту точку мы смещаем на случайную величину и повторяем разбиение и смещение для каждого из полученных под-отрезков. И так далее — пока отрезки не станут длиной в один пиксель. Вот и весь алгоритм. Важное замечание - случайные смещения должны быть пропорциональны длинам отрезков, на которых производится разбиения. Например, мы разбиваем отрезок длиной l — тогда точка посередине него должна иметь высоту
$$h = (h_L + h_R) / 2 + random(- R * l, R * l)$$ (hL и hR — высоты на левом и правом конце отрезка, а константа R определяет «шероховатость» получающейся ломаной и является главным параметром в данном алгоритме).

 Попробуем обобщить этот алгоритм для двумерной карты высот. Начнем с присвоения случайных высот четырем углам всей карты целиком и разобьём её (для удобства мы предполагаем, что работаем с квадратной картой, причем её сторона является степенью двойки) на четыре равных квадрата. В каждом из них известно значение в одном из углов. Где взять остальные?
 Всё той же интерполяцией, как и в одномерном midpoint displacement — точка в центре получается усреднением высот всех 4 угловых точек, а каждая серединная точка на стороне большого квадрата — усреднением пары точек, лежащих на концах соответствующей стороны. Осталось привнести немного шума — сдвинуть случайным образом центральную точку вверх или вниз (в пределах, пропорциональных стороне квадрата) — и можно повторять рекурсивно наши действия для полученных под-квадратов.
 Это ещё не diamond-square — данный алгоритм, как правило, тоже называют алгоритмом midpoint displacement и несмотря на то, что он дает уже относительно приемлемые результаты, в получившейся картинке без особого труда можно заметить её «прямолинейную» натуру.
 Метод diamond-square — дающий возможность получать фрактальные, более настоящие рельефы, отличается от двумерного midpoint displacement тем, что состоит из двух попеременно повторяющихся шагов:
 1) square — точно так же устанавливает центральную точку в квадрате путем усреднения угловых и добавлением случайного отклонения;
 2) diamond — необходим чтобы установить высоту точек, лежащих в серединах сторон. Тут усредняются не только 2 боковые точки — «сверху» и «снизу» (в случае если говорить о точках в вертикальной стороне), но и другая пара точек «слева» и «справа» — в таком случае мы берем 2 дополнительные точки, полученные в шаге «square», центральные точки.
 Также важно отметить то, что данные 2 высоты, которые были получены нами в прошлом шаге, должны быть ранее посчитаны — по этой причине обсчет крайне обязательно осуществлять последовательно, сперва для всех квадратов осуществить этап «square», далее для всех ромбов осуществить этап «diamond», и затем перейти к меньшим квадратам, повторяя данные шаги столько раз, сколько нам необходимо для достижения желаемого результата.
 В зависимости от количества пройденных итераций, будет меняться "детализация" итогового ландшафта

 Также имеется еще одна проблема при генерации рельефа на краях нашей карты. Проблема в том, что на этапе «diamond» «метод может применяет высоту точек, которые находятся за границами нынешнего квадрата и, вероятно, всей карты». Вариантов решения данной проблемы можно придумать несколько, мы выделим два из них:
 1) или рассматривать данные высоты одинаковыми (0 либо 1, либо любой иной константе; это, к слову, практично с целью погружения сторон нашего рельефа под воду);
 2) или показать, что наша область будто бы скручена в тор, и, когда пытаемся выяснить высоту точки, лежащей с одного края нашего ландшафта, в качестве соседней, мы будим брать точку, лежащую на том же уровне с противоположного края данного рельефа. Реализуется весьма просто — нам поможет взятие координат по модулю, равному размеру карты.
 Результатом работы данного алгоритма является подобный ландшафт:

Направление: