Создание объекта с нуля (от А до Я)


Урок рассчитан на пользователей хотя бы с элементарными навыками в Фотошопе.
Для работы нам потребуются: Workshop, MilkShape, Photoshop. С вашего позволения дальше я их буду писать на русском. В Милкшейп обязательно должны быть установлены плагины для wso, в Фотошоп плагины для dds. (смотреть соответствующие темы)

В этом уроке я попробую научить вас делать свои объекты с созданием меша и созданием своей текстурной карты. Для примера мы возьмем не очень сложный объект, но и не совсем простой, чтоб вы видели, как делается нестандартный меш. Урок очень большой, поэтому все части я оформляю под спойлер.

SpoilerTarget">Изготовление меша высокой детализации

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

Справа находится окно управления. Во вкладке Model кнопки корректировки и создания простых фигур. Давайте сделаем простую вазу. Ваза симметричная со всех сторон и не имеет углов, поэтому логично, что наиболее подходящая фигура для ее изготовления – это цилиндр.

Нажимаем на кнопку Cylinder. Открылось дополнительное меню, в котором нам нужно сразу рассчитать, сколько у нашей вазы будет полигонов. Для новичков это может показаться сложным, все достигается опытом. Тут не следует забывать, что готовый объект не должен превышать 1200 полигонов. А у нашей вазы будет еще внутренняя сторона, значит эта часть не должна превысить 600. Контролируется это во вкладке Tools-Show Model Statistics.
Sliсes – сколько точек будет по окружности, чем меньше, тем угловатей фигура. Stacks – сколько точек будет по длине цилиндра, т.е. мест, где наш цилиндр мы сможем изгибать. Всегда прибавляем 1 stacks наверху для того, чтобы плавно загнуть край. Я возьму для нашей вазы 10х16, вполне оптимально.
После того, как выставили параметры, в левом верхнем окне просто растягиваем наш цилиндр.

Нажимает кнопку Select, кликаем по чистому полю и снимаем выделение.

Основа готова. Теперь нам нужно придать вазе форму. Для этого во вкладке Select нажимаем внизу Vertex (т.е. будем выделять повертексно). Обязательно убираем галочку Ignore Backfaces в закладке Select, если она у вас стоит. Мышкой выделяем нужную линию вертексов. Т.к. у нас симметричная фигура, выделяем сразу всю линию.

Переходим во вкладку Scale. Внизу параметры по осям. Ставим в каждом окошке через точку, если хотим уменьшить выделенный фрагмент, то меньше единицы (с минусом), если увеличить, то больше единицы. Советую ставить самый маленький интервал, чтобы увеличение шло плавно. Я обычно ставлю для уменьшения -0.99, а для увеличения 1.01.

Сейчас будем уменьшать. Нажимаем на кнопку Sсale рядом с параметрами (четное количество раз! Иначе у вас могут перевернуться полигоны). Я нажала шесть раз, сразу стало заметно округление нижнего края.
Дальше делаем все тоже самое с остальными линиями, пока не добьетесь той фигуры, что вам нужна. Уменьшаем, увеличиваем их по вашему желанию. Также линии можно передвигать вверх и вниз. Для этого (при выделенной линии) переходим в Move и мышкой двигаем.

После манипуляций у вас получится своя ваза, советую делать по какой-либо картинке, так проще. У меня получилось вот так. Наверху оставлены вертексы для сведения с внутренним слоем.

Переходим во вкладку Select-Face и в окне Top нажимаем на центральную точку. У нас выделилась окружность и прилегающие к ней полигоны (но так же выделилось и донышко).

Зажимаем шифт и правой кнопкой мыши снимаем выделение с донышка (нам его трогать не надо).

Затем кнопкой Delete на клавиатуре удаляем лишние фейсы по горлышку вазы. Ваза стала полой.

Все, что в левом нижнем окне показано белым будет видеться в игре – все что черным будет прозрачным (это обратная сторона фейсов), поэтому нашей вазе необходим второй внутренний слой.

Выделяем всю вазу без самой верхней окружности вертексов. Нажимаем Edit-Dublicate Selection. Мы продублировали нашу вазу, теперь их две.

Не снимая выделения, чуть чуть уменьшаем наш внутренний слой (Scale). Затем также не снимая выделения, нажимаем в меню Face-Reverse Vertex Order и Face-Smoose All (этим мы переворачиваем все полигоны в обратную сторону. Ваза внутри стала белой, как и снаружи.

Выделяем кромку (повертексно). Немного уменьшая ее (Scale) и функций Move опускаем до уровня соединения с внутренним слоем. Если этого не сделать, то край вазы будет резко обрываться и в игре будет разделение на внешний и внутренний слои.

Можете еще поиграться с формой, при этом не забывайте уже выделять оба слоя.
Если вы решили, что ваза закончена, то самое время сохранить проект (File-Save) и перейти к группам и текстурной развертке.


SpoilerTarget">Создание текстурной карты в Милкшейп

Откроем Фотошоп. Создаем новый документ размером 512*512 и заливаем его любым темным цветом, например черным, сохраняем в png. Это будет ваш основной файл фона для Милкшейпа, вы будете использовать его во всех своих объектах.

Идем обратно в Милкшейп, переходим на вкладку Groups. Если у вас все правильно, в этом окне сейчас должны быть две группы (две ваши фигуры – внешний и внутренний слои вазы. Сколько вы сделаете отдельных фигур, столько и будет групп. Пока вы не сделали карту, объединять группы нельзя). Для удобства можете группы переименовать (Rename), а то если их много, можно потом в карте запутаться.
Встаем на каждую группу по очереди и нажимаем Select, у нас выделилась вся ваза.

Переходим во вкладку Materials, нам нужно добавить сюда свой материал, чтобы мы смогли увидеть карту. Нажимаем New, в окошке появился шарик, кликаем на первое слово none и выбираем наш файл с черным фоном (шарик стал черным или того цвета, какой у вас фон), затем жмем Assign (накладываем эту текстуру на нашу фигуру).

Не снимая выделения жмем Ctrl+T (в меню Window-Texture Coordinat Editor). Ура, мы видим нашу карту. Сбоку переход между группами. Будьте тут аккуратны и не нажимайте лишних кнопок, от карты зависит вся ваша текстуризация объекта.

Если вы попереключаете по группам, то увидите, что они перекрывают друг друга, т.е. если вы в фотошопе наложите текстуру, она ляжет и на внутренний и на внешний слой одновременно. А нам так не надо, нам нужно, чтобы у вазы на внешнем слое был один рисунок, на внутреннем другой. Поэтому нам надо подвинуть карты так, чтобы они друг друга не перекрыли.
Не снимая выделения, нажимаем на Scale и уменьшаем карту мышкой (до разумных пределов), оттаскиваем (Move) ее в сторону, затем во второй группе делаем тоже самое и оттаскиваем ее в другую сторону.



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

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


SpoilerTarget">Клонирование объекта в Воркшопе и экспорт меша

Открываем Воркшоп. Идем в раздел Объекты-Декор-различный декор и выбираем себе любой предмет для клонирования (это тот файл, на который вы потом будете импортировать свой объект). Я обычно беру вот этот предмет декора.

Заполняем поля Title – всегда пишите какие-то свои уникальные данные, чтоб название не могло повториться или совпасть ни с каким другим. В поле Description смело вписываем свое имя, потому что этот предмет – ваша авторская работа.

Переходим во вкладку Mesh и экспортируем меш этой коровки в свою рабочую папочку.


SpoilerTarget">Добавление тени и привязка костей

Переходим назад в Милкшейп. Открываем экспортированный меш прямо в нашем проекте с вазой.

Смотрим на пропорцию. Как видно, моя вазочка очень большая.

В данный момент она выделена, я просто применяю функцию Scale и уменьшаю ее всю пропорционально.

Затем снимаем все выделение (Edit-Select none), переходим во вкладку Groups и видим, что у нас добавилось две группы. Одна группа это тень, вторая сам импортированный объект. Выделяем группу с ненужным объектом и удаляем.

Тень оставляем и двигаем вверх, она должна быть самой первой (Up). Если тень маловата, увеличиваем ее и подвигаем под наш объект (Scale, Move).

Назначение костей. Если вы не назначите кости своему объекту, в игре он не будет видеться (будет прозрачным). Переходим во вкладку Groups и выделяем все наши группы (кроме тени).

Затем переходим во вкладку Jonts. Мы видим, что там появилась одна кость. Если вы заходили сюда ранее, то видели здесь пустое место. Эта кость появилась с тенью. Нажимаем Assign - этим мы привязываем этим нашу кость ко всем выделенным группам объекта.


SpoilerTarget">Группировка и редактирование текстурной карты

Меш готов, однако если мы глянем меш в Воркшопе, то увидим там две группы – тень и объект, а у нас их три и если мы экспортируем меш так, то Воркшоп выдаст ошибку. Нам надо объединить группы в одну. Тень всегда отдельно! Выделяем группы нашего объекта (без тени) и нажимаем Regroup.
Нажимаем контрл+Т, проверяем карту, теперь у нас все на одном поле, ничего не должно пересекаться. Если что-то не так, делаем контрл+Z и двигаем группы на карте по новой, затем опять объединяем группы. Должно получиться так:

Еще небольшой нюанс, мы двигали вертексы по нашей вазе вверх и вниз, а на карте они расположены на одном расстоянии друг от друга, если оставить такую карту и наложить на нее рисунок, он может непропорционально растянуться.
Поэтому кропотливо выделяем функцией Scale строчки вертексов и приблизительно поднимаем/опускаем Move их также, как на нашей вазе. Большой точности здесь не нужно.

Если карта вас устраивает, копируем ее кнопкой PrtScr (принтскрин) в буфер обмена.
Экспортируем меш. File-Export-TSRW Objects (имя можно дать любое, но для себя пометьте, что это меш hide). Милкшейп не закрывайте.


SpoilerTarget">Создание текстур в Фотошопе

Переходим в Фотошоп. Создаем новый файл размером 512*512. Вставляем из буфера обмена нашу карту (контрл+V), подвигаем ее так, чтобы она была ровно на весь файл.



Осталось наложить текстуру. Самое сложное – найти такую текстуру. Это должна быть развертка. Т.е. мы видим что наша ваза на карте прямоугольная, т.е. она развернута на плоскости.
Я нашла в интернете вот такую текстуру для верхнего слоя (внутренний будет просто гладкий):

Накладываем нашу текстуру на карту так, чтобы полностью перекрыть края.

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

Так как рисунок будет "огибать" всю вазу, я его сделала двойным. Качество вашего Мультиплеер напрямую зависит от вашего владения фотошопом. Учитывайте, что текстура должна иметь ровные края, и рисунок должен сходиться. Если это не так, дорабатывайте кистями и фильтрами.
По возможности, можно и внутреннюю часть немного отредактировать, например наложить тень по ободку. В данном случае я просто выделила по карте прямоугольный блок и кисточкой-затемнителем чуть-чуть наложила тень.

Объединяем все слои Layer-Flattern Image и сохраняем файл как dds с названием «ваза multiplier»

Создание маски. Если у вас не предполагается различный перекрас предмета, то маска – это файл 512*512, полностью залитый красным цветом, в формает dds. Если у вас в предмете две или три зоны перекраса, то на исходной карте в фотошопе выделяете область 1-го перекраса и заливаете красным, область второго перекраса – зеленым, область третьего – синим. Для наглядности сделаем в нашей вазе две зоны перекраса. Я выделяю часть, где внешний слой – красным, а часть карты с внутренним слоем зеленым. Вот такая карта у меня получилась:



Так же объединяем все слои и сохраняем с именем «ваза mask»

Создание Specular. Спекулар отвечает за блеск нашего объекта, поэтому он делается из Мультиплеера, просто добавляется контрастности и яркости. Все что белое будет светиться, все темное наоборот будет матовым. Тут зависит от объекта, если вам нужно чтоб он сверкал – делаете белее, если нужно, чтоб был матовым, то темнее.

Для правильного Спекулара нужно еще добавить черный Альфа-канал. Вкладка Channels, контрл+А, добавить канал по выделению и залить черным.

Объединяем слои и сохраняем с именем «ваза specular».


SpoilerTarget">Сведение меша и текстур в Воркшопе

Текстуры готовы, теперь необходимо все свести в Воркшопе. Переходим в Воркшоп. Во вкладке Textures оставляем только одну позицию, остальные удаляем. Клик справа по треугольничку - Delete.

Ипортируем меш своей вазы (вкладка mesh, кнопка с зеленной стрелкой). Импортируем во вкладку hide (высокая детализация), к low и shadow вернемся позже.

Теперь меняем все текстуры на свои (Маску, Мультиплеер и Спекулар), на вопрос изменить размер, отвечаем «да».
Вот что на этом этапе получилось у меня:


SpoilerTarget">Изменение паттернов и экспорт в сим3пак

Теперь займемся паттернами. Под основными текстурами, которые мы импортировали находятся 3 вкладки с паттернами. Первая из них перекрасит все, что залито в нашей Маске красным, вторая зеленым, третья синим. Если у вас в маске всего две зоны перекраса, то третий паттерн можно отключить (false).
Итак, подбираем паттерн под внешний слой вазы. Я взяла Металл – chrome01 и выставила понравившийся мне цвет.

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

Третьей зоны у нас нет, я этот паттерн отключила.

Меш Low и Shadow. Одного меша с высокой детализацией не хватит, в объекте еще есть меш с низкой детализацией (мы видим его в игре при загрузке предмета) и Shadow – солнечная тень. Сейчас можно увидеть, что наша ваза отбрасывает тень от старого объекта.
Конечно, заново мы эти меши делать не будем, мы их сделаем из нашего меша с высокой детализацией.
Для этого вернемся в Милкшейп. На вкладке Groups удалим группу с тенью, она не нужна. У нас осталась одна объединенная группа. Применяем к ней плагин уменьшения полигонов – Tools –DirectX Mesh Tools.

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

Я остановилась на цифре 340 (уменьшила вполовину), нажимаем на дискетку – Сохранить.
Наша фигура стала черной. Это произошло из-за того, что плагин поломал все фейсы и они перевернулись.

Заходим в меню Face-Smoose All (перевернуть фейсы). Фигура стало опять белой.

После этой операции пропадает привязка к костям. Нам нужно опять их привязать. Для этого выделяем получившийся уменьшенный по полигонам объект, импортируем наш меш hide, переходим в Joints, нажимаем Assign и удаляем загруженный меш hide. В результате у вас должна остаться одна группа с объектом с уменьшенными полигонами и с привязанными костями.

Все, экспортируем этот объект с названием «ваза напольная low».

Переходим в Воркшоп и импортируем этот меш Low во все три оставшиеся вкладки - в меш Low и в оба меша Shadow



Осталось проставить цену на вкладке Project

Можете еще добавить иконку в поле Launcher tunbnail. Иконка это изображение в любом формате (jpg. gif. png) размером 256*256 с изображением предмета и вашим логотипом. Это, что будет отображаться в Лаунчере (S3 Wiewer).

Экспортируем в формате sim3pack (желательно с тем же именем, которое вы писали в поле Title).

Ставим в игру, проверяем )) Надеюсь, у вас все получилось.


Аналогичная техника применяется для отображения изображений с МРТ , metaball-ов и для вокселизации рельефа.

В этой части я расскажу о технике создания разрушаемого рельефа Marching Cubes, а в более общем применении - для создания плавного граничного меша твёрдого объекта. В этой статье мы начнём с рассмотрения двухмерной техники, затем трёхмерной, а в третьей части рассмотрим Dual Contouring. Dual Contouring - это более совершенная техника, создающая тот же эффект.

Наша цель

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

Функция - это отличный способ описания произвольной фигуры. но она не помогает нам отрисовать её.

Для отрисовки нам нужно знать её границу , например, точки между положительными и отрицательными значениями, где функция пересекает ноль. Алгоритм Marching Cubes берёт такую функцию и создаёт полигональную аппроксимацию её границы, которую можно использовать для рендеринга. В 2d эта граница будет непрерывной линией. При переходе в 3d она становится мешем.

Реализация двухмерных Marching Cubes

Примечание: код на python , в котором содержится откомментированный код со всем необходимым.

Для простоты давайте начнём с 2d, а позже перейдём к 3d. Я буду называть алгоритмы в 2d и в 3d «Marching Cubes», потому что по сути они являются одним алгоритмом.

Шаг 1

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

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

Шаг 2

Затем мы обрабатываем каждую ячейку отдельно, заполняя её соответствующей границей.
Простая таблица поиска обеспечивает 16 возможных комбинаций углов, находящихся снаружи или внутри. В каждом случае она определяет, какая граница должна быть отрисована.
Все сочетания двухмерных marching cubes

Шаг 3

После повторения процесса для всех ячеек границы соединяются, создавая готовый меш, даже несмотря на то, что каждая ячейка рассматривалась независимо.
Отлично! Думаю, это в целом походит на исходный круг, описанный формулой. Но как видите, он весь изломан, а отрезки расположены под углами в 45 градусов. Так получилось, потому что мы решили выбрать вершины границ (красные квадраты), равноудалённые от точек ячейки.

Адаптивность

Лучшим способом избавиться от углов в 45 градусов будет адаптивный алгоритм marching cubes. Вместо простого задания всех вершин границ из центральных точек ячеек их можно расположить так, чтобы они лучше всего соответствовали сплошной области. Для этого нам нужно не только знать, находится ли точка внутри или снаружи; нам требуется также знать, насколько она глубоко расположена .

Это значит, что нам нужна какая-то функция, дающая нам меру того, насколько глубоко точка находится внутри/снаружи. Она не обязана быть точной, потому что мы используем её только для аппроксимаций. В случае нашего идеального круга, имеющего радиус в 2,5 единиц, мы применим следующую функцию .


В которой положительные значения находятся внутри, а отрицательные - снаружи.

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


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

Часть 2. Трёхмерные Marching Cubes

Итак, в 2D мы разбиваем пространство на сетку, а затем для каждой вершины ячейки вычисляем, где находится эта точка - внутри или снаружи сплошной области. В 2d-сетке у каждого квадрата по 4 угла, и для каждого из них есть два варианта, то есть у каждой ячейки существует возможных комбинаций состояний углов.

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

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

Плохая новость заключается в том, что у куба 8 углов, то есть существует рассматриваемых возможных случаев. И некоторые из этих случаев гораздо более сложны, чем в 2D.

Очень хорошая новость заключается в том, что нам совершенно не нужно в этом разбираться. Вы можете просто скопировать собранные мной случаи и перейти сразу к разделу с результатами («Соединяем всё вместе»), не задумываясь обо всех сложностях. А потом начать читать о dual contouring, если вам нужна более мощная техника.

Все сложности

Примечание: в этом туториале больше рассматриваются концепции и идеи, чем методы реализации и код. Если вам больше интересна реализация, то изучите реализацию в 3D на python , в которой содержится откомментированный код со всем необходимым.

Вы всё ещё читаете? Отлично, мне это нравится.

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


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

Остальные два случая можно найти простым поворотом первого случая.

Мы можем использовать ещё один трюк:


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

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

Единственный разумный человек

Если прочитать или большинство туториалов по Marching Cubes, то в них говорится, что необходимо всего 15 случаев. Как же так? Ну, на самом деле это правда - три нижних случая с моей схемы не обязательно нужны. Вот снова три этих случая в сравнении с противоположными им другими случаями, дающими схожую поверхность.
И второй, и третий столбцы правильно отделяют сплошные углы от пустых. Но только когда мы рассматривает один куб в отдельности. Если посмотреть на рёбра каждой грани ячейки, то можно увидеть, что они различаются для второго и третьего столбцов. Инвертированные не будут правильно соединяться с соседними клетками, оставляя отверстия в поверхности. После добавления лишних трёх случаев все ячейки правильно соединяются.

Соединяем всё вместе

Как и в двухмерном случае, мы можем просто обработать все ячейки независимо. Вот сферический меш, созданных из Marching Cubes.


Как видите, форма сферы в целом сделана правильно, но в отдельных частях есть хаос из узких сгенерированных треугольников. Можно решить эту проблему с помощью алгоритма Dual Contouring, который является более совершенным, чем Marching Cubes.

Часть 3. Dual Contouring

Marching Cubes просты в реализации, поэтому часто используются. Но у алгоритма есть множество проблем:


Что же нам делать?

На сцене появляется Dual Contouring

Примечание: в этом туториале больше рассматриваются концепции и идеи, чем методы реализации и код. Если вам больше интересна реализация, то изучите реализацию на python, в которой содержится откомментированный код со всем необходимым ( и ).

Dual Contouring решает эти проблемы и при этом гораздо более расширяем. Его недостаток заключается в том, что нам потребуется ещё больше информации об , то есть о функции, определяющей, что является сплошным и пустым. Нам нужно знать не только значение , но и градиент . Эта дополнительная информация улучшит адаптивность по сравнению с marching cubes.

Dual Contouring помещает в каждую ячейку по одной вершине, а затем «соединяет точки», создавая полный меш. Точки соединяются вдоль каждого ребра, имеющего смену знака, как и в marching cubes.


Примечание: слово «dual» («двойственный») в названии появилось потому, что ячейки в сетки становятся вершинами меша, что связывает нас с двойственным графом .

В отличие от Marching Cubes, мы не можем вычислять ячейки по отдельности. Чтобы «соединять точки» и найти полный меш, мы должны рассматривать соседние ячейки. Но на самом деле это намного более простой алгоритм , чем Marching Cubes, потому что здесь нет множества отдельных случаев. Мы просто находим каждое ребро со сменой знака и соединяем вершины ячеек, соседних с этим ребром.

Получение градиента

В нашем простом примере с 2d-кругом радиуса 2,5 задаётся следующим образом:
(другими словами, 2,5 минус расстояние от центральной точки)

Воспользовавшись дифференциальным исчислением, мы можем вычислить градиент:


Градиент - это пара чисел для каждой точки, обозначающих, насколько изменяется функция при движении по оси x или y.

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


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

Адаптивность

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

Мы хотим выбрать точку, наиболее близко соответствующую полученной нами информации, т.е. вычисленному значению

И градиенту. Заметьте, что мы сэмплировали градиент вдоль рёбер, а не в углах.


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

Переходим в 3d

Случаи в 2d и в 3d на самом деле не очень отличаются. Ячейка теперь является кубом, а не квадратом. Мы выводим грани, а не рёбра. Но на этом различия заканчиваются. Процедура выбора одной точки в ячейке выглядит так же. И мы по-прежнему находим рёбра со сменой знака, а затем соединяем точки соседних ячеек, но теперь уже четырёх ячеек, что даёт нам четырёхсторонний полигон:


Грань, связанная с отдельным ребром. У неё есть точки в каждой соседней ячейке.

Результаты

Dual contouring создаёт гораздо более естественные формы, чем marching cubes, что можно увидеть на примере созданной с его помощью сферы:


В 3d эта процедура достаточно надёжна, чтобы выбирать точки, находящиеся вдоль ребра острого участка и выбора углов при их возникновении.

Проблемы

Колинеарные нормали

Большинство туториалов останавливается на этом, но у алгоритма есть небольшой грязный секрет - решение QEF в соответствии с описанием в оригинальной статье про Dual Contouring на самом деле работает не очень хорошо.

Решив QEF, мы можем найти точку, наиболее соответствующую нормалям функции. Но на самом деле нет никаких гарантий, что получившаяся точка находится внутри ячейки .

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


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

Техника 1: решение QEF с ограничениями

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

Техника 2: смещение QEF

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

Благодаря этому решение всего QEF стягивается к центру.

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

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

Подробнее обе техники показаны в коде .

Самопересечения

Ещё одна проблема dual contouring заключается в том, что иногда он может генерировать самопересекающуюся 3d-поверхность. В большинстве случаев на это не обращают внимания, так что я не решал эту проблему.

Существует статья, в которой рассказывается о её решении: «Intersection-free Contouring on An Octree Grid», Ju and Udeshi, 2006

Однородность

Хотя получаемый dual contouring меш всегда герметичен, поверхность не всегда является хорошо заданной. Так как на ячейку приходится всего одна точка, при прохождении через ячейку двух поверхностей она будет общей для них. Это называется «однородным» мешем и может вызывать проблемы у некоторых алгоритмов текстурирования. Проблема часто возникает, когда сплошные объекты тоньше, чем размер ячейки или несколько объектов почти касаются друг друга.
Обработка таких случаев является значительным расширением функционала базового Dual Contouring. Если вам нужна эта функция, то рекомендую изучить эту реализацию Dual Contouring или

Расширение алгоритма

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

Другое интересное расширение заключается в том, что для Dual Contouring нам необходимы всего лишь определение того, что находится внутри/снаружи, и соответствующие нормали. Хотя я говорил, что у нас для этого есть функция, мы можем извлечь ту же самую информацию из другого меша. Это позволяет нам выполнить «ремеш», т.е. сгенерировать чистое множество вершин и граней, очищающих исходный меш. В качестве примера можно привести модификатор remesh из Blender.

Дополнительное чтение

  • Dual Contouring - это одна из множества похожих техник. См. другие подходы со своими плюсами и минусами в списке SwiftCoder .

Теги: Добавить метки

Меш (Mesh) или Градиентная сетка подходит и для EPS8 и для EPS10. Единственное, что Mesh лучше изначально рисовать в Adobe Illustrator, так как при сохранении в EPS из других программ (например, CorelDraw) меш может растрироваться. (А может и нет, тут уж сами проверяйте). Урок по основам меша представил мне Гринько Валерий, за что ему огромное спасибо, а так же честь и хвала. 🙂 Для отрисовки меша для стоков сначала необходимо выбрать фото. И фотография должна быть именно своя. Не пытайтесь искать хорошее фото в интернете в надежде, что автор его не узнает, что вы его отрисовали. И не пытайтесь изменять его. Лучше возьмите свое фото, пусть не такое красивое, но все же свое. Тем более, что для отрисовки Mesh вам не понадобится исключительное качество фотографии. Сам автор этого урока уже «погорел» на такой оплошности. Отрисовал чужую фотографию коктейля, за что удалили его аккаунт. И теперь он вынужден раскручивать свой аккаунт снова.

По поводу съемки фотографии. Снимайте в естественном свете, то есть без вспышки, и тогда блики и тени, которые вы будете отрисовывать с помощью Mesh, будут иметь красивый естественный вид. Фото необходимо вставить в Adobe Illustrator и заблокировать слой, на котором он находится. Саму градиентную сетку можно делать двумя способами. Мы рассмотрим только их принципы.

Первый способ.

1. Создать новый слой и на нем нарисовать фигуру по форме совпадающую с объектом. На примере мы нарисовали окружность, по форме близкой к вишне.
2. А затем эту фигуру нужно переделать в градиентную сетку инструментом Mesh (U), кликая им по середине. В результате чего появится сетка.
3. Слой с мешем следует поместить под слой с фотографией. В нашем случае это слой Layer2. 4. А после чего приступить к самому главному, к моделированию сетки по форме вишни. Добавление новых узлов сетки производим инструментом Mesh (U), а их перемещение и выделение белой стрелочкой Direst Selection (A). Сколько нужно создать узлов? Если создать слишком мало, форма получится слишком гладкой и только издали будет подходить на рисуемый объект. Если сразу создать слишком много, то можно запутаться с редактированием. Поэтому нужно стараться максимально минимальным количеством узлов сетки создать приемлемую форму. Ограничений здесь нет, речь только о целесообразности. Узлов должно быть больше там, где есть изменение цвета.
5. После того, как сетка создана, можно переходить раскрашиванию. Образцы цвета можно брать прямо с фотографии. Сначала активируем белую стрелочку (A), затем выбираем инструмент Пипетка Eyedropper (I). Для того, чтобы выбрать точку зажимаем , то есть временно активируем белую стрелочку, щелкаем по нужной точке на Mesh, которой хотим придать цвет, затем отпускаем и щелкаем на фотографии на то место, где нужно этот цвет получить. Для того, чтобы выделить несколько точек и задать им один цвет, дополнительно к нужно нажать еще и . Переключая видимость слоя с фотографией мы можем отслеживать изменение в нашем моделируемом объекте.
Сначала мы получаем нечто такое, а потом добавляя точки, перемещая их и задавая им цвет, мы доводим форму до совершенства. Тут не лишними были бы знания и чувство формы и цвета, но, думаю, что многое приходит с опытом.

Второй способ.

Иногда форма объекта бывает очень сложной и построение сетки Mesh может быть осложнено тем, что она будет вести себя непредсказуемо. Тогда Mesh можно сделать другими способом: сделать Mesh из прямоугольника, а затем применить к нему маску в виде искомой формы.

1. Создаем прямоугольник и делаем из него Mesh точно так же, то есть инструментом Mesh (U) щелкаем по прямоугольнику, чтобы появилась сетка. 2. Создаем форму объекта, в нашем примере это будет круг. Помещаем его поверх прямоугольника-меша и делаем из него маску, то есть выделяем эти два объекта из из контекстного меню, вызванного правой кнопкой мыши, выбираем Make Clipping Mask.
3. Далее раскраску производим таким же образом.

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

Вот, например, готовый рисунок, который предоставил мне Валерий. Если у вас есть чем дополнить этот урок, прошу написать об этом в комментариях. А так же огромная просьба, если у вас есть собственные наработки в стоковом векторе , поделитесь своими секретами. Уроки могут приниматься в любом виде, главное, чтобы рисунки были именно ваши.


Эта страница была показана 41007 раза.

Таблица 11.6. Фонемы американского английского

Значение

Значения IPA, показанные в таблице 11.2, являются индексами массива лицевых мешей фонем, используемого при визуализации. Для создания последовательности анимации, соедините значения IPA, образуя таким образом слова и предложения. Вы узнаете больше о создании последовательностей звуков в разделе "Создание последовательностей фонем", расположенном далее в этой главе.

Возвратимся к лицевым анимациям. Для создания законченной системы лицевой анимации необходимо анимировать (или скомбинировать) разнообразные меши, представляющие фонемы и выражения лица.

Создание лицевых мешей

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

Здесь и расположена самая сложная часть воплощения лицевой анимации - создание разнообразных лицевых мешей, используемых движком. Используя различные программы трехмерного моделирования, такие как trueSpace фирмы Caligari или Poser фирмы Curious Labs, вы можете создавать лицевые меши быстро и легко.

Морфируемаялицеваяанимация

TrueSpace фирмы Caligari (версия 5.1 и новее) комплектуется плагином3 Facial Animator, который очень помогает при создании, текстурировании и анимировании лицевых мешей. Я использовал плагин Facial Animator для создания демонстрационной программы этой главы.

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

Независимо от используемого трехмерного пакета все сводится к одной вещи - созданию базового лицевого меша.

Создание базового меша

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

Опять же, независимо от используемого пакета вам необходимо создать базовый меш. Помните, что этот базовый меш не должен содержать никаких выражений: рот должен быть закрыт, а глаза полностью открыты. Для упрощения, я собираюсь использовать один из лицевых, мешей, поставляемых в наборе с Facial Animator программы trueSpace. На рис. 11.3 показан базовый меш, который я буду использовать в этой главе.

После выбора меша, используемого в качестве базового, вам необходимо корректно текстурировать его. Замечательным свойством плагина Facial Animator программы trueSpace является то, что вы можете создавать карты текстур, используемые лицевым мешем, взяв картинки самогосебя, сделанные сбоку и спереди, и натянуть их на меш, используя инструмент Texturize Head. Для этой демонстрационной программы я использовал свое собственное лицо при текстурировании меша.

Замечание. Лицевой меш Chris (содержащий малое количество граней), который я использую в качестве базового, имеет несколько недостатков, самым заметным их которых является отсутствие глаз. Я использовал инструмент AddFace программы trueSpace для добавления нескольких граней, представляющих собой глаза, что позволило мне наложить карту текстуры на них.

3. Плагин (plug-in) - подключаемый программный модуль, который расширяет возможности родительской программы. - Примеч. науч. ред.

Рис. 11.3. Лицевой меш Chris (содержащий малое количество граней) из программы trueSpace замечательно послужит в качестве базового меша. Помня о лицензии фирмы Caligari, поставляемой для каждого пользователя, вы можете свободно изменять модель Chris в ваших приложениях

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

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

Создание выражений лица

Прежде чем продолжить, убедитесь, что вы сохранили базовый меш на диск, используя описательное имя, например, Base.x. Помните, вы используете формат.X, так что вы можете захотеть экспортировать меш в качестве.X файла. Если такой возможности нет, экспортируйте меш как файл.3DS. После того как вы выполнили это, вы можете использовать программу Conv3DS.exe, поставляемую с DirectX SDK, для

в директории \Bin\DXUtils DirectX SDK, или вы можете скачать последнюю ее версию с сайта http://www.microsoft.com/DirectX кopпopaции Microsoft.

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

Так получается, что Facial Animator поставляется с набором предопределенных выражений лица, которые вы можете накладывать на лицевой меш простым щелчком мыши! Если задуматься, у Poser имеются точно такие же возможности, так что вы можете создавать выражения лица в любой из этих программ!

Для создания выражений лица меша щелкните закладку Expressions (Выражения) в диалоговом окне Facial Animator. Как вы можете видеть на рис. 11.4, появляется список выражений, которые можно применить к мешу.

Рис. 11.4. Список Expressions плагина Facial Animator предоставляет вам восемь выражений, которые вы можете выбрать

Я хочу, чтобы демонстрационная программа была проста, и т. к. я достаточно счастливый парень, я хочу использовать выражение Smile (Улыбка). Щелкните на кнопку Smile, и вы увидите, как лицевой меш в трехмерном редакторе изменится в соответствии с выбранным выражением. Если вам интересно, щелкните на кнопках других выражений, чтобы увидеть как они влияют на меш. После того, как вы закончите, щелкните на кнопку Smile, чтобы вернуться к настройке улыбающегося меша.

После того как вы выбрали используемое выражение лица (в данном случае улыбку), экспортируйте меш. Для упрощения, назовите его Smile.x. Поместите файл Smile.x в ту же директорию, что и Base.x. Если вы хотите использовать большее количество выражений, щелкните на соответствующую кнопку выражения в диалогом окне Facial Animator, подождите изменения меша и экспортируйте его в.X файл.

Я не хочу вводить вас в заблуждение тем, что в Facial Animator только восемь выражений, поэтому щелкните на вкладку Gestures (Жесты). Вот! Должно появиться еще четырнадцать выражений (см. рис. 11.5).

Рис. 11.5. Список Gestures плагина Facial Animator содержит еще 14 выражений, которые вы можете применять к мешу

Прежде чем использовать выражения вкладки Gestures, щелкните один раз кнопку Reset All (Сбросить все). Это вернет лицевой меш к начальной ориентации. Не стесняйтесь использовать набор жестов. Решите, какой жест вы хотите использовать, и экспортируйте меш, использующий его. Для своей демонстрационной программы я использую только жест Blink (Моргать).

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

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

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

Аналогичная техника применяется для отображения изображений с МРТ , metaball-ов и для вокселизации рельефа.

В этой части я расскажу о технике создания разрушаемого рельефа Marching Cubes, а в более общем применении - для создания плавного граничного меша твёрдого объекта. В этой статье мы начнём с рассмотрения двухмерной техники, затем трёхмерной, а в третьей части рассмотрим Dual Contouring. Dual Contouring - это более совершенная техника, создающая тот же эффект.

Наша цель

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

Функция - это отличный способ описания произвольной фигуры. но она не помогает нам отрисовать её.

Для отрисовки нам нужно знать её границу , например, точки между положительными и отрицательными значениями, где функция пересекает ноль. Алгоритм Marching Cubes берёт такую функцию и создаёт полигональную аппроксимацию её границы, которую можно использовать для рендеринга. В 2d эта граница будет непрерывной линией. При переходе в 3d она становится мешем.

Реализация двухмерных Marching Cubes

Примечание: код на python , в котором содержится откомментированный код со всем необходимым.

Для простоты давайте начнём с 2d, а позже перейдём к 3d. Я буду называть алгоритмы в 2d и в 3d «Marching Cubes», потому что по сути они являются одним алгоритмом.

Шаг 1

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

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

Шаг 2

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

В которой положительные значения находятся внутри, а отрицательные - снаружи.

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

Если соединить всё вместе, то это будет выглядеть так:

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

Часть 2. Трёхмерные Marching Cubes

Итак, в 2D мы разбиваем пространство на сетку, а затем для каждой вершины ячейки вычисляем, где находится эта точка - внутри или снаружи сплошной области. В 2d-сетке у каждого квадрата по 4 угла, и для каждого из них есть два варианта, то есть у каждой ячейки существует возможных комбинаций состояний углов.

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

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

Плохая новость заключается в том, что у куба 8 углов, то есть существует рассматриваемых возможных случаев. И некоторые из этих случаев гораздо более сложны, чем в 2D.

Очень хорошая новость заключается в том, что нам совершенно не нужно в этом разбираться. Вы можете просто скопировать собранные мной случаи и перейти сразу к разделу с результатами («Соединяем всё вместе»), не задумываясь обо всех сложностях. А потом начать читать о dual contouring, если вам нужна более мощная техника.

Все сложности

Примечание: в этом туториале больше рассматриваются концепции и идеи, чем методы реализации и код. Если вам больше интересна реализация, то изучите реализацию в 3D на python , в которой содержится откомментированный код со всем необходимым.

Вы всё ещё читаете? Отлично, мне это нравится.

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


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

Остальные два случая можно найти простым поворотом первого случая.

Мы можем использовать ещё один трюк:



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

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


Единственный разумный человек

Dual Contouring решает эти проблемы и при этом гораздо более расширяем. Его недостаток заключается в том, что нам потребуется ещё больше информации об , то есть о функции, определяющей, что является сплошным и пустым. Нам нужно знать не только значение , но и градиент . Эта дополнительная информация улучшит адаптивность по сравнению с marching cubes.

Dual Contouring помещает в каждую ячейку по одной вершине, а затем «соединяет точки», создавая полный меш. Точки соединяются вдоль каждого ребра, имеющего смену знака, как и в marching cubes.

Примечание: слово «dual» («двойственный») в названии появилось потому, что ячейки в сетки становятся вершинами меша, что связывает нас с двойственным графом .

В отличие от Marching Cubes, мы не можем вычислять ячейки по отдельности. Чтобы «соединять точки» и найти полный меш, мы должны рассматривать соседние ячейки. Но на самом деле это намного более простой алгоритм , чем Marching Cubes, потому что здесь нет множества отдельных случаев. Мы просто находим каждое ребро со сменой знака и соединяем вершины ячеек, соседних с этим ребром.

Получение градиента

В нашем простом примере с 2d-кругом радиуса 2,5 задаётся следующим образом:

(другими словами, 2,5 минус расстояние от центральной точки)

Воспользовавшись дифференциальным исчислением, мы можем вычислить градиент:

Градиент - это пара чисел для каждой точки, обозначающих, насколько изменяется функция при движении по оси x или y.

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

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

Адаптивность

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

Мы хотим выбрать точку, наиболее близко соответствующую полученной нами информации, т.е. вычисленному значению

и градиенту. Заметьте, что мы сэмплировали градиент вдоль рёбер, а не в углах.

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

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

Переходим в 3d

Случаи в 2d и в 3d на самом деле не очень отличаются. Ячейка теперь является кубом, а не квадратом. Мы выводим грани, а не рёбра. Но на этом различия заканчиваются. Процедура выбора одной точки в ячейке выглядит так же. И мы по-прежнему находим рёбра со сменой знака, а затем соединяем точки соседних ячеек, но теперь уже четырёх ячеек, что даёт нам четырёхсторонний полигон:

Грань, связанная с отдельным ребром. У неё есть точки в каждой соседней ячейке.

Результаты

Dual contouring создаёт гораздо более естественные формы, чем marching cubes, что можно увидеть на примере созданной с его помощью сферы:

В 3d эта процедура достаточно надёжна, чтобы выбирать точки, находящиеся вдоль ребра острого участка и выбора углов при их возникновении.

Выбор местоположения вершины

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

В 3d проблема ещё более усугубляется, потому что здесь становится больше нормалей.

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

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


С математической точки зрения отдельные штрафные функции являются квадратом расстояния от идеальной линии для текущей нормали. Сумма всех квадратных членов является квадратичной функцией , поэтому общая штрафная функция называется QEF (quadratic error function, функцией квадратичной ошибки). Нахождение минимальной точки квадратичной функции - это стандартная процедура, имеющаяся в большинстве библиотек работы с матрицами.

Проблемы

Колинеарные нормали

Большинство туториалов останавливается на этом, но у алгоритма есть небольшой грязный секрет - решение QEF в соответствии с описанием в оригинальной статье про Dual Contouring на самом деле работает не очень хорошо.

Решив QEF, мы можем найти точку, наиболее соответствующую нормалям функции. Но на самом деле нет никаких гарантий, что получившаяся точка находится внутри ячейки .

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

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

Техника 1: решение QEF с ограничениями

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

Техника 2: смещение QEF

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

Благодаря этому решение всего QEF стягивается к центру.

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

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

Подробнее обе техники показаны в коде .

Самопересечения

Ещё одна проблема dual contouring заключается в том, что иногда он может генерировать самопересекающуюся 3d-поверхность. В большинстве случаев на это не обращают внимания, так что я не решал эту проблему.

Существует статья, в которой рассказывается о её решении: «Intersection-free Contouring on An Octree Grid», Ju and Udeshi, 2006

Однородность

Хотя получаемый dual contouring меш всегда герметичен, поверхность не всегда является хорошо заданной. Так как на ячейку приходится всего одна точка, при прохождении через ячейку двух поверхностей она будет общей для них. Это называется «однородным» мешем и может вызывать проблемы у некоторых алгоритмов текстурирования. Проблема часто возникает, когда сплошные объекты тоньше, чем размер ячейки или несколько объектов почти касаются друг друга.

Обработка таких случаев является значительным расширением функционала базового Dual Contouring. Если вам нужна эта функция, то рекомендую изучить эту реализацию Dual Contouring или

Расширение алгоритма

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

Другое интересное расширение заключается в том, что для Dual Contouring нам необходимы всего лишь определение того, что находится внутри/снаружи, и соответствующие нормали. Хотя я говорил, что у нас для этого есть функция, мы можем извлечь ту же самую информацию из другого меша. Это позволяет нам выполнить «ремеш», т.е. сгенерировать чистое множество вершин и граней, очищающих исходный меш. В качестве примера можно привести модификатор remesh из Blender.

Дополнительное чтение

  • Dual Contouring - это одна из множества похожих техник. См. другие подходы со своими плюсами и минусами в


Эта статья также доступна на следующих языках: Тайский

  • Next

    Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

    • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

      • Next

        В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

  • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
    https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png