Проблема формирования алмазно-квадратной местности

11

Я реализовал алгоритм алмазного квадрата в соответствии с этой статьей: http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2

Проблема в том, что я получаю эти крутые скалы по всей карте. Это происходит по краям, когда ландшафт рекурсивно подразделяется:

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

Вот источник:

void DiamondSquare(unsigned x1,unsigned y1,unsigned x2,unsigned y2,float range)
    {      
    int c1 = (int)x2 - (int)x1;
    int c2 = (int)y2 - (int)y1;
    unsigned hx = (x2 - x1)/2;
    unsigned hy = (y2 - y1)/2;
    if((c1 <= 1) || (c2 <= 1))
            return;

// Diamond stage
float a = m_heightmap[x1][y1];
float b = m_heightmap[x2][y1];
float c = m_heightmap[x1][y2];
float d = m_heightmap[x2][y2];
float e = (a+b+c+d) / 4 + GetRnd() * range;

m_heightmap[x1 + hx][y1 + hy] = e;

// Square stage
float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;
float g = (a + b + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y1] = g;
float h = (b + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x2][y1+hy] = h;
float i = (c + d + e + e) / 4 + GetRnd() * range;
m_heightmap[x1+hx][y2] = i;

DiamondSquare(x1, y1, x1+hx, y1+hy, range / 2.0);   // Upper left
DiamondSquare(x1+hx, y1, x2, y1+hy, range / 2.0);   // Upper right
DiamondSquare(x1, y1+hy, x1+hx, y2, range / 2.0);   // Lower left
DiamondSquare(x1+hx, y1+hy, x2, y2, range / 2.0);       // Lower right

}

Параметры: (x1, y1), (x2, y2) - координаты, которые определяют регион на карте высот (по умолчанию (0,0) (128,128)). дальность действия - в основном макс. рост. (по умолчанию 32)

Помощь будет принята с благодарностью.

Кафка
источник
Если не смотреть внимательно на ваш код, похоже, что в 4-х рекурсивных вызовах в конце вы, вероятно, ошиблись в неправильных вызовах. Карта выглядит так, как будто каждый квадрат поворачивается / переворачивается перед тем, как вычислять следующий набор, таким образом разделяя карту на странные скалы. Нижний край верхнего правого квадрата выглядит так, как будто он совпадает с правым краем верхнего левого квадрата, и так далее.
DampeS8N
Я не уверен, что ты имеешь в виду. Центр системы координат находится в верхнем левом углу, ось x направлена ​​вправо, а y - вниз. Таким образом, на первой итерации (x1 = 0, y1 = 0), (x2 = 128, y2 = 128) и (x1 + hx = 64, y1 + hy = 64) - центр квадрата. Квадрат, таким образом, делится на 4 подквадрата: ((0,0) (64,64)), ((64,0) (128,64)), ((0,64) (64,128)) и ((64, 64) (128128)). Выглядит хорошо для меня ...
Кафка

Ответы:

12

На каждом уровне подразделения «квадратный» шаг зависит от результатов «алмазного шага». Но это также влияет на алмазную ступеньку, произведенную в соседней ячейке, которую вы не учитываете. Я бы переписал функцию DiamondSquare для итерации в ширину, а не в глубину, как у вас сейчас.

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

P = (J + G + K + E)/4 + RAND(d)

но ваш код эффективно делает

P = (J + G + J + E)/4 + RAND(d)

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

Вот мой код и вывод:

void DiamondSquare(unsigned x1, unsigned y1, unsigned x2, unsigned y2, float range, unsigned level) {
    if (level < 1) return;

    // diamonds
    for (unsigned i = x1 + level; i < x2; i += level)
        for (unsigned j = y1 + level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2] = (a + b + c + d) / 4 + GetRnd() * range;
        }

    // squares
    for (unsigned i = x1 + 2 * level; i < x2; i += level)
        for (unsigned j = y1 + 2 * level; j < y2; j += level) {
            float a = m_heightmap[i - level][j - level];
            float b = m_heightmap[i][j - level];
            float c = m_heightmap[i - level][j];
            float d = m_heightmap[i][j];
            float e = m_heightmap[i - level / 2][j - level / 2];

            float f = m_heightmap[i - level][j - level / 2] = (a + c + e + m_heightmap[i - 3 * level / 2][j - level / 2]) / 4 + GetRnd() * range;
            float g = m_heightmap[i - level / 2][j - level] = (a + b + e + m_heightmap[i - level / 2][j - 3 * level / 2]) / 4 + GetRnd() * range;
        }

    DiamondSquare(x1, y1, x2, y2, range / 2, level / 2);
}

http://i.imgur.com/laBhN.png

Джимми
источник
Да, я также размышлял в духе подхода «в ширину». Эти фракталы всегда вызывают у меня проблемы. То же самое было с шумом Перлина и L-системами. Ты восхитителен.
Кафка
3

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

Для квадрата вы рассчитываете высоту точек с

float f = (a + c + e + e) / 4 + GetRnd() * range;
m_heightmap[x1][y1+hy] = f;

который алгоритм страницы указывает на использование, если вы оборачиваете свою карту. Это создает впечатление, что вы используете значение высоты "next square over" для вычисления этого значения. В простейшем первом случае центральная точка (с высотой 'e') используется как слева, так и справа для вычисления f.

Однако алгоритм, на который вы ссылаетесь, использует фактические значения других квадратов / ромбов, чтобы помочь вам вычислить значение высоты этой квадратной точки. В их алгоритме точка второго уровня вычисляется по следующей формуле:

N = (K + A + J + F)/4 + RAND(d)

Заметили отсутствие дублирования значения там?

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

F = (A + C + E)/3 + ...
    instead of
F = (A + C + E + E)/4 + ...
Fnord
источник
Спасибо, это было полезное наблюдение. Я думаю, что научился поражению не переходить непосредственно к кодированию, когда я вижу уравнения.
Кафка
Не за что. Я тоже сам много времени делаю ... "Смотри, я могу что-то кодировать. Должен. Кодируй. Сейчас!"
фнорд