Геймдизайнер-телепат
0,0
рейтинг
10 июля 2013 в 11:01

Разработка → Бесконечные неповторяющиеся текстуры с помощью мозаики Вана



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

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

Как же быть? Есть один трюк — непериодические мозаики. Они лишены проблемы повторяемости и достаточно просты в реализации. Одну из таких мозаик придумал китайский математик Ван Хао в 1961 году. Элементы этой мозаики можно представить в виде прямоугольников с разноцветными гранями. Но чтобы понять принцип её работы, надо сначала разобраться в классическом методе заполнения площадей текстурами.


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



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



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



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

К сожалению, размер атласа для полного набора плиток очень быстро растёт с количеством вариаций граней. Расчёт ведётся по следующей формуле: N2*M2, где N — количество горизонтальных граней, M — количество вертикальных граней. Например, для двух вариантов на горизонталь и вертикаль выходит шестнадцать разных плиток. Если просто нужна гарантированная возможность продолжить мозаику, то формула будет попроще: 2*N*M.



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



Как создать атлас плиток? Можно взять одну большую картинку и наложить на неё сверху сетку из цветных граней. Затем выбрать подходящий шов для каждого цвета и размножить его по сетке. Либо пойти от обратного: сначала сделать сетку, потом заполнить пустые места. Ниже есть ссылки на более подробную инструкцию.



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

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



Я набросал нехитрый код на C# для Unity3d для работы с мозаикой Вана, можете посмотреть на него по ссылккам ниже.

Unity Web Player | Windows | Linux | Mac | Исходники на GitHub

Пробел — новая случайная текстура, 1, 2, 3 — выбор атласа, Esc — выход.
Для пользователей Linux: Сделайте файл WangTiles исполняемым с помощью «chmod +x WangTiles» и запускайте.

Полезные ссылки по теме:
Инструкция по созданию атласов
Процедурная генерация подземелий
Подробнее про создание атласов разных размеров
Полуавтоматический генератор атласов

По просьбам трудящихся небольшое дополнение про создание атласов:

Берём понравившуюся большую картинку, накладываем сверху сетку для удобства в фотошопе или где ещё.



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



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



Дальше без элементарного программирования не обойтись, можно собрать всё руками, но на это уйдёт куча времени. Используете в коде созданный атлас, выбираете из него кусочки и делаете мозаику. Элементарно. Сложнее всего смастерить красивый атлас, но с текстурами всегда так.

На самом деле я просто пересказал инструкцию отсюда. Почитайте, там всё хорошо расписано.

Даниил Басманов @BasmanovDaniil
карма
190,2
рейтинг 0,0
Геймдизайнер-телепат
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (25)

  • +1
    Что мешает в этом случае использовать обычный random?
    • +5
      Обычный рандом это какой? Вас наверное смутили иллюстрации, вообще мозаику можно запросто использовать для фотореалистичных текстур, но мои способности художника пока не позволяют рисовать что-либо пиксель арта. Вот, например, переделанные текстуры из Minecraft:



      Слева оригинал, справа мозаика из атласа с увеличенной и немного перемешанной текстурой
      • +1
        Возможно, просто было мало примеров из «жизни»
        • +14
          На скорую руку сваял мозаику из этой текстуры. Над швами сильно не работал, но, думаю, смысл понятен.

          • +1
            У меня тут в офисе похожий палас лежит (цвета, правда, чуть другие, да и чуть затоптан местами :) ). Грешным делом, подумал, что Вы у себя под ногами половое покрытие сфотографировали :))
            • +2
              напольное ;)
              • +1
                Судя по минусам, что-то в этой фразе читателей смущает, да ))
    • +1
      Для полного рандома надо, чтобы каждая плитка из атласа сочеталась с каждой (либо перебирать рандомом, пока не совпадут грани), а заодно рандомные значения зависели от некого seed и не менялись с каждым запуском или вообще с каждой отрисовкой.
      Оба ограничения не актуальны, если можно «запечь» мозаику, а в игре уже ее отрисовывать, но для этого нужен промежуточный слой — текстура или текстурные координаты (если у нас детальная полигональная сетка). Но гораздо удобнее было бы сделать шейдер для отрисовки текстуры из атласа по некому алгоритму без промежуточных расчетов и лишних инструкций.
  • +10
    Мои глаза! Не могли на какой-нибудь траве показывать? :(
    • +1
      Сделал цвета немного помягче
  • 0
    Интересный подход.
    А можно ли как-то переделать алгоритм, чтобы он был не итеративным, а позиционным. То есть, я передаю ему координаты ячейки, а он мне индекс тайла в атласе. Это было бы идеально для текстурирования «на лету» сразу в пиксельном шейдере.
    Конечно, полноценная переделка вряд-ли возможна, но можно, наверное, как-то разбить тайлы на подмножества, чтобы в любой ячейке по координатам можно было бы определить подмножество и рандомно выбирать тайл из него.
    • 0
      Алгоритм создания мозаики Вана полностью локален, если вам известны цвета соседей, то выбрать из атласа множество подходящих тайлов совсем не сложно, нужно всего пара условий. У меня в коде просто строит проверка «если правая грань соседа слева такая, то выбираем один из этих столбцов в атласе», потом аналогичным образом выбираю колонку и получаю нужный тайл.
      • 0
        В том-то и дело, что в шейдере состояние соседей не известно, так как эти состояния нигде не хранятся. Но если алгоритм будет повторяемым, то можно будет лишь гарантировать, что сверху — снизу — справа — слева определенная группа тайлов, но не ясно какой конкретно.
      • 0
        Как раз по ссылке на GPU Gems 2 описан подход для текстурирования подобным методом на лету, то есть в пиксельном шейдере. То, что я и искал.
  • 0
    Отличный материал, осталось дело за малым — всем авторам движков добавить поддержку таких текстур =) на уровне движка
  • 0
    Постановка задачи напомнила одну популярную статью на этом ресурсе.
  • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    Если честно, то момент с контейнерами абсолютно не понял. Картинки сколько не рассматривал — ясности не добавило. Принцип «стыковки» сторон — понятен, а дальше — тупик. Какой контейнер, из чего он получается, и почему он «хорошо укладывается» — вообще не могу «сложить паззл».
    • 0
      Представьте, что у вас есть ящик советской кафельной плитки. Плитка кривая и плохо стыкуется, приходиться долго подбирать подходящую, чтобы она не упиралась в соседей. Теперь представьте, что вместо кривой плитки у вас есть ровная и качественная, и вы можете положить любую плитку куда угодно и она будет хорошо смотреться. Получается, что для гладкого и красивого пола в ванной важна не середина, а края плитки. Рисунок на любой плитке можно поменять, и она всё равно впишется в окружение, потому что её края сочетаются с соседями. Если плитка не квадратная, а фигурная, то менять плитки немного сложнее, но рисунок по-прежнему играет второстепенную роль, он может иметь больше или меньше деталей, может быть с вариациями или без. Плитка делится на два компонента: края, которые определяют возможность укладывания плитки, и лицевую поверхность, которая отвечает за общий внешний вид и может меняться.
      • 0
        Спасибо, с утра почитаю вдумчиво :-)
    • +1
      В статье очень непонятно описан процесс. А вот ссылки по теме — раскрывают смысл очень хорошо:
      смотрите тут: http.developer.nvidia.com/GPUGems2/gpugems2_chapter12.html и тут: www.pathofexile.com/forum/view-thread/55091
      • 0
        О! И правда, прояснилось. Спасибо.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Обновил пост, смотрите дополнение в конце статьи.
      • НЛО прилетело и опубликовало эту надпись здесь

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