Алгоритм смещения средней точки

14

MDPMDP

Этот вопрос возник в основном из отчаяния , после нескольких часов попыток выяснить проблему.

Если вы посмотрите на картинку выше, вы должны увидеть, что мой алгоритм смещения средней точки работает (несколько) успешно; в создании несколько когерентной картины шума.

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

unsigned char** mdp(unsigned char** base, unsigned base_n, unsigned char r) {
    size_t n = (2 * base_n) - 1;

    unsigned char** map = new unsigned char*[n];
    for (unsigned i = 0; i < n; ++i) map[i] = new unsigned char[n];

    // Resize
    // 1 0 1
    // 0 0 0
    // 1 0 1
    for (size_t i = 0; i < n; i += 2) {
        for (size_t j = !(i % 2 == 0); j < n; j += 2) {
            map[i][j] = base[i / 2][j / 2];
        }
    }

    // Diamond algorithm
    // 0 0 0
    // 0 X 0
    // 0 0 0
    for (size_t i = 1; i < n; i += 2) {
        for (size_t j = 1; j < n; j += 2) {
            unsigned char& map_ij = map[i][j];

            unsigned char a = map[i - 1][j - 1];
            unsigned char b = map[i - 1][j + 1];
            unsigned char c = map[i + 1][j - 1];
            unsigned char d = map[i + 1][j + 1];
            map_ij = (a + b + c + d) / 4;

            unsigned char rv = std::rand() % r;
            if (map_ij + r < 255) map_ij += rv; // EDIT: <-- thanks! the bug! `map_ij + rv`, not `r`
            else map_ij = 255;
        }
    }

    // Square algorithm
    // 0 1 0
    // 1 0 1
    // 0 1 0
    for (size_t i = 0; i < n; ++i) {
        for (size_t j = (i % 2 == 0); j < n; j += 2) {
            unsigned char& map_ij = map[i][j];

            // get surrounding values
            unsigned char a = 0, b = a, c = a, d = a;
            if (i != 0) a = map[i - 1][j];
            if (j != 0) b = map[i][j - 1];
            if (j + 1 != n) c = map[i][j + 1];
            if (i + 1 != n) d = map[i + 1][j];

            // average calculation
            if (i == 0) map_ij = (b + c + d) / 3;
            else if (j == 0) map_ij = (a + c + d) / 3;
            else if (j + 1 == n) map_ij = (a + b + d) / 3;
            else if (i + 1 == n) map_ij = (a + b + c) / 3;
            else map_ij = (a + b + c + d) / 4;

            unsigned char rv = std::rand() % r;
            if (map_ij + r < 255) map_ij += rv;
            else map_ij = 255;
        }

    }

    return map;
}

Если у вас есть какие-либо советы или ресурсы, отличные от http://www.gameprogrammer.com/fractal.html и http://www.lighthouse3d.com/opengl/terrain/index.php?mpd2 для создания фрактальной местности, я бы цените их как комментарии также.

Редактировать:

MDP

Это новое изображение, согласно предложению Фабиана (ty), однако у него все еще есть некоторые странные причуды, которые вы должны увидеть сразу (маленькие «ямочки» везде).

Что может быть причиной этого странного поведения? Обновленный исходный код: http://www.pastie.org/1924223

Редактировать:

Большое спасибо Фабиану за обнаружение ошибки проверки границ, для тех, кто заинтересован, вот текущее решение в виде 512x512 png. И текущий исходный код (измененный Фабианом) . MDP

Изменить (несколько лет спустя): версия Python https://gist.github.com/dcousens/5573724#file-mdp-py

deceleratedcaviar
источник
На фотографиях похоже, что каждая из точек находится на одной высоте. Находятся ли углы на одной высоте?
deft_code
1
Для чего это стоит, ваши изображения выглядят очень красиво. :)
ChrisE
scrand (): я не совсем уверен, что понимаю - должен ли он возвращать подписанный символ на интервале (-r / 2, r / 2]? Ямочки в любом случае кажутся чем-то вроде результата из-за переполнения. Соседние области, кажется, внезапно становятся черными, а затем поднимаются обратно к белому. В целом, это также выглядит, как будто есть резкие полосы, снова предлагая мне, что вы переполнены, возможно. более высокая точность (скажем, целое число), а затем ограничение значений до диапазона [0,256] или [-128,127]?
ChrisE
Проблема была решена ниже, потому что я проверял границы диапазона случайного значения, а не его фактического значения. Функция scrand () представляла собой временную функцию «шума», в идеале возвращающуюся [-128, 127]
замедленная
Ах, круто! Рад слышать, что это работает сейчас.
ChrisE

Ответы:

12

Алгоритм рекурсивно добавляет значение, но значение может быть положительным или отрицательным (обычно + -1 / (2 ^ октава))

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

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

РЕДАКТИРОВАТЬ

Итак, в main (64 >> i) нужно изменить еще две вещи, чтобы получить половину эффекта в каждой октаве, а также вашу функцию вывода (ту, которая отображает финальную [] [] tp imgdta [], вы просто нужно положить в

imgdta [(i * n) + j] = 128 + final [i] [j];

а не блок if else.

Другое дело, я не уверен, почему, но ваша проверка границ не удалась (это строки 38 и 65), если вы полностью удалите проверку, вы также заметите некоторые новые темные пятна, поэтому я считаю, что вам может потребоваться перейти на более крупный тип перед тем, как делать границы, проверьте, хотите ли вы получить более шумную картинку с "64 / i".

ДРУГОЕ РЕДАКТИРОВАНИЕ

только что выяснил, что это было, вы сравниваете с 'r', а не 'rv', в проверке границ. Вот исправленный код: http://pastie.org/1927076

Ричард Фабиан
источник
Это определенно был лучший способ сделать это, но пока нет сигары, похоже, у моего изображения все еще есть некоторые «причуды», я обновил исходный пост.
замедленная
не уверен, но строка 93 выглядит неправильно, 64 / i, возможно, должно быть 64 >> i (так как вы вдвое меньше, чем каждая октава)
Ричард Фабиан
Ааа !! Большое спасибо, я не могу в это поверить, я знал, что это будет глупо для второй проблемы. Мне понравился ваш временный код TGA, извините, я должен был избавить вас от хлопот и поставить заголовок.
замедленная
3

Две вещи, которые выскакивают:

  1. У вас есть веская причина сделать это в фиксированной точке? В этом нет ничего плохого, и есть много причин для его использования (особенно это касается требований к памяти, если вы планируете идти к ОГРОМНОЙ карте), но я бы определенно начал с версии алгоритма с плавающей запятой. и преобразовать его в фиксированную точку после того, как он заработал; это должно, если не что иное, устранить один вероятный источник ошибок (в частности, я подозреваю, что ваши ограничения могут вызывать проблемы и условия, когда нужно добавить / вычесть rv).
  2. Хотя это трудно понять из кода, который я вижу, не похоже, что ваше случайное смещение высоты масштабируется с уровнем, и это в сочетании с проблемой в (1) может вызывать некоторые проблемы - вы не должны вытесняя на одинаковую сумму на каждом уровне.

Да, и одна неалгоритмическая вещь: я настоятельно рекомендую не делать выделения в вашей функции mdp (); передать два разных уже распределенных массива и выполнить итерацию «на месте», переходя от одного к другому. Если ничего другого, это позволит вам пинг-понг взад и вперед по мере выполнения слоев, вместо того, чтобы каждый раз выделять новый массив.

Стивен Стадницки
источник
Некоторые хорошие моменты, очевидно, я просто пытаюсь получить правильный алгоритм, реализация далека от идеала, я даже не очищаю память на этом этапе: P.
замедленная
На данный момент масштабирование по проходам составляет 64 / i, очевидно, я изменю это позже, но на самом деле это не объясняет эффект ямочек, который ощущается в настоящее время. : S
замедленная
0

В дополнение к вышесказанному, вы в настоящее время не удаляете выделенную память. Чтобы исправить это, измените строку 104 с:

for (unsigned i = 1; i < 6; ++i) final = mdp(final, n, 64 / i);

в

for (unsigned i = 1; i < 6; ++i) {
  signed char** new_final = mdp(final, n, 64 / i);
  for (unsigned i = 0; i < n; ++i)
    delete[] final[i];
  delete[] final;
  final = new_final;
}

и добавьте это после записи в файл tga:

for (unsigned i = 0; i < n; ++i)
  delete[] final[i];
delete[] final;
ДМА
источник
Я очень хорошо знаю, как очистить память, но, учитывая, что это был только прототип алгоритма и будет переписан для использования векторов, я просто хотел убедиться, что это правильно.
замедленная