Это развитие идей из публикации Визуализация при помощи генеративных алгоритмов: гифа, деревья, повторяющиеся и дифференциальные линии (на Python), однако вам необязательно читать ее — все проекты, описанные здесь самодостаточны.
Все что вам необходимо знать: последнее время автору были особенно интересны биологические образы и дифференциальный рост. В посте есть результаты экспериментов с различными биологическими и физическими закономерностями, в частности дифференциальной сеткой, в том числе в 3D, и трещинами.
Дифференциальная сетка [GITHUB]
После того как я получил дифференциальную линию (подробнее в посте на HABR), очевидным следующим шагом было попытаться создать нечто подобное в трех измерениях. И единственный вариант — это заставить саму линию двигаться в трех измерениях. Однако, это не выглядит очень интересным. Вместо этого, я решил выбрать сочетание формы поведения «притяжение/отклонение» дифференциальной линии с функцией роста Гифы. В общем-то, я хотел сделать треугольные сетки, которые росли бы наружу без самопересечения или столкновений. Делать это непосредственно в трех измерениях казалось немного трудным, поэтому я решил начать с двухмерной версии.
В гифке выше вы можете увидеть, что новые треугольники добавляются к внешней стороне существующей сетки. Они добавляются так, чтобы не сталкиваться с существующими частями сетки. Я использую очень простое обнаружение столкновений, где перед добавлением нового узла проверяется, не слишком ли близко потенциальная новая вершина к любым существующим вершинам. Вдобавок все вершины всё ещё ведут себя, как в дифференциальной линии, то есть, они пытаются заполучить как можно больше свободного места вокруг себя, оставаясь при этом достаточно близко к своим соседям.
Основной момент в работе с сеткой — заставить сетку делать различные операции самобалансировки в процессе роста. Есть несколько вещей, которые вы можете попробовать:
- Разделите слишком длинные края.
- Избегайте вставки новых вершин, где плотность вершин уже слишком высокая.
- Переворачивайте рёбра, чтобы уменьшить длину.
- Добавьте силу, которая будет заставлять треугольники пытаться быть приблизительно равносторонними.
В приведенной выше анимации используются все эти принципы.
Теперь мы уменьшаем размер треугольника и добавляем немного структурности.
Расчёт был на то, что этот набор правил будет напоминать рост слизи или, что вероятнее, лишайника. Оказывается, стандартная версия этого алгоритма очень похожа на определенные виды лишайников. И это похоже ещё на рост печеночных мхов, как подсказали мне в Твиттере.
Изначально я контролировал рост, при помощи выбора случайных рёбер, проверке на то, внешние ли они и определении, могу ли я прикрепить новый треугольник к этому ребру. Проблема этой стратегии в том, что в конечном итоге большинство выбранных рёбер будут на внутренней стороне структуры. Это означает, что вы потратите много ресурсов на проверки рёбер, с которыми вы потом ничего не сделаете.
Есть довольно очевидное решение этой проблемы. (И, как обычно, когда я говорю очевидно, я имею в виду, что на понимание этого у меня ушло несколько месяцев). Вместо того, чтобы выбирать случайные рёбра и проверять, можно ли на них сделать новый треугольник, вы можете использовать «источники пищи». Сначала вы случайным образом позиционируете источники пищи по всему пространству. Тогда у вас будет что-то происходить каждый раз, когда ребро в структуре натыкается на один из этих источников. Например, если вы резко увеличите темп роста, когда сетка добирается до источника пищи, вы получите что-то вроде этого:
Если вы дадите этой симуляции поработать некоторое время, то сможете получить много интересных результатов. Вот версия, где цвет каждого треугольника постепенно меняется в зависимости от того, когда он был создан. Цвет постепенно меняется от белого к черному, а затем мгновенно переходит обратно в белый. В первый раз я сделал это случайно, но, как вы можете видеть, это придаёт визуализации отличный трехмерный эффект.
Дифференциальная сетка в 3D [GITHUB]
Так как дифференциальная сетка, похоже, работала неплохо, я решил сделать попытку воссоздать то же поведение в трёх измерениях. В некотором смысле это «просто» вопрос добавления третьего измерения и работы в нём. Тем не менее, было несколько других проблем.
Во-первых, избегать столкновений немного труднее в 3D. Во-вторых, сделать сетку последовательной тоже сложнее. (Я использую вариацию Half-Edge структуры данных для представления сетки и всех операций на ней.)
Подобно дифференциальной линии и дифференциальной сетке, проблема столкновений и самопересечений тут совсем не решена. Путём регулировки различных сил можно заставить сетку вести себя достаточно предсказуемо, чтобы обнаружение столкновений не потребовалось (в большинстве случаев...). Я должен также отметить, что это первый раз, когда я пытаюсь написать генеративную 3D сетку, так что я могу гарантировать, что есть лучшие способы сделать это.
Одним из таких «лучших» способов является Floraform, созданный Nervous System. Другим (хотя, грубо говоря, это не сетка) является Сellular Form Энди Ломаса.
Если вы хотите поэкспериментировать с 3D-сетками самостоятельно, я рекомендую попробовать Blender, который имеет полную поддержку Python API. Вы можете увидеть мой пример реализации здесь. Вы также можете попробовать Processing в сочетании с HE_Mesh Фредерика Вануэтта.
Это изображение рендеринга сетки сделано в Blender, с помощью движка Blender Cycles Rendering. Как вы можете видеть, у объекта на изображении довольно «потрёпанный» вид. Это результат выбранной стратегии роста: как и в методе источника пищи, используемом для создания дифференциальной сетки, рост этой сетки контролируется путем применения параметра «интенсивности роста». Вершины с высокой интенсивностью роста будут более склонны двигаться, чем вершины с более низкой, что в свою очередь приводит к добавлению новых вершины к сетке.
При запуске моделирования некоторым вершинам задана интенсивность роста 1, в то время, как у остальных вершин интенсивность роста равна 0. По мере моделирования, интенсивность роста будет постепенно распространяться на соседние вершины, подобно тому, как тепло распространяется по куску металла.
Для того, чтобы сетка росла наружу, внешним рёбрам сетки случайным образом задана интенсивность роста 1 в ходе моделирования. Если мы сделаем это рассеянным образом, мы получим потрёпанные сетки, как на изображении сверху. С другой стороны, если применить интенсивность роста на краю более равномерно, результаты будут больше похожи на второе изображение
Трещины [GITHUB]
Я уже упоминал Джареда Тарбелла в этом тексте, но упомяну его еще раз. Одним из первых увиденных мной произведений генеративного искусства, на которое я обратил внимание, было Substrate Тарбелла. Это гениальный алгоритм во всей его простоте. Линии растут в определенном направлении до тех пор, пока они не достигнут границы территории, или до тех пор, пока не столкнутся с другой линией. Когда линия останавливается, по крайней мере одна новая линия появляется перпендикулярно одной из существующих линий в произвольном положении.
21.png
Воссоздание похожих на города структур Substrate само по себе упражнение интересное. Однако, я хотел объединить (частично) поведение Substrate с другими особенностями ранее упомянутого алгоритма пересечения листьев. Цель была имитировать сетку трещин, которые можно увидеть на хрупких материалах, таких как стекло или фарфор. Вы, возможно, видели это сами, если вам хоть раз приходилось ронять телефон или если у вас есть старая посуда. В изобразительном искусстве эти трещины называются Кракелюр.
Подобно методу, используемому в алгоритме пересечения листьев, мы распространяем несколько источников пищи по пространству. Затем мы генерируем любое количество изначальных трещин.
Последующие этапы роста этих трещин контролируются следующим алгоритмом:
- Найти все источники в поле зрения трещины (FOV). Изменение поля зрения даст очень разные результаты; большее FOV сделает трещины более извилистыми.
- Рассчитать (массу) центр источников в поле зрения.
- Сделать шаг к центру масс.
- Мы завершаем трещину, если она столкнулся с другой или если нет источников в FOV.
В дополнение к контролю роста трещин, мы должны добавлять новые трещины. Стратегия, используемая в изображениях, показанных здесь, заключается в том, чтобы случайным образом создавать новые трещины от кончика трещин, которые ещё активны (т.е. ещё не столкнулись ни с чем). Частота возникновения новых трещин существенно повлияет на конечный результат.
Если вы когда-то видели разбитый экран телефона, вы могли заметить, что трещины редко распределены равномерно: плотность трещин, как правило, больше вблизи точки удара. Для имитации этого эффекта вы можете ввести скорость трещин. По мере роста трещины скорость постепенно уменьшается. Когда появляется новая трещина, она наследует значение (например, 0,8) текущей скорости пркдешественницы. В этом случае скорость трещин используется не для контроля темпов роста, а для регуляции скорости создания новых трещин. Этот эффект выглядит как на следующем изображении.