Триангуляция что это в математике
Математика в Gamedev по-простому. Триангуляции и Triangle.Net в Unity
Всем привет! Меня зовут Гриша, и я основатель CGDevs. Математика – очень крутой инструмент при разработке игр. Но если скажем без понимания векторов и матриц обойтись в принципе сложно, то алгоритмы триангуляций не столь обязательная вещь, но с помощью них решается достаточно большое количество интересных задач. Сегодня хотелось бы поговорить про достаточно важный инструмент в вычислительной геометрии, такой как триангуляции и их применение в игровой индустрии. Кроме того, я написал порт и немного обёрток великолепной библиотеки Triangle.Net для Unity. Если интересно – добро пожаловать под кат. Ссылка на гитхаб прилагается.
Что такое триангуляция?
В общем случае триангуляция – это разбиение геометрического объекта на треугольники. Это понятие само по себе довольно простое. Базовый пример триангуляции в случае игровых движков – это меш. Строго говоря меш может состоять не только из треугольников, но в игровых движках по целому ряду причин берутся именно меши, состоящие из треугольников.
Триангуляции бывают разными, но один из самых популярных видов триангуляций является триангуляция Делоне. Этот вид триангуляции отличается тем свойством, что в окружности, описанной вокруг каждого треугольника, лежат только вершины этого треугольника.
Зачем они нужны?
В целом за пределами игровой индустрии с помощью триангуляций решается большое количество задач. В геймдеве же первая задача, которая приходит на ум – это navigation mesh. Навмеш – это структура данных, которая определяет, по какому пространству игрок может ходить. Он позволяет избежать таких сложных вычислительных задач, как определение столкновений с частью окружения.
Вторая интересная задача, решаемая с помощью триангуляции Делоне в геймдеве – это генерация террейнов и объектов представленных в виде множества точек. Основным плюсом триангуляции Делоне в данном случае является то, что исходя из её свойств она позволяет избежать очень острых треугольников, которые будут мешать и создавать артефакты на тиррейне.
Помимо этого, с помощью триангуляции Делоне с ограничениями и таким алгоритмам как Chew’s second algorithm и Ruppert’s algorithm можно генерировать сетки ещё лучше для тиррейнов и генерировать хорошие сетки для другого применения – метода конечных элементов.
Сам по себе метод конечных элементов, это один из методов с помощью которого решаются задачи прикладной физике. В геймдеве он позволяет решать многие задачи, связанные с симуляцией деформаций, жидкостных симуляций и другого используемого для спец. эффектов. Обычно для записи эффектов в анимации, так как для реалтайма метод обладает слишком высокой вычислительной сложностью. Важной деталью при использовании метода является то, что ошибка метода зависит от углов треугольников в сетке. При наличии в сетке очень острых углов метод даёт огромную ошибку, по этой причине нужны алгоритмы, перечисленные выше.
Ну и конечно же в целом процедурная генерация мешей. Как пример в гитхаб проекте приведена сцена с возможностью рисовать меши. Некоторые физические головоломки используют это применение, как основную механику. Но кроме того алгоритмы триангуляции позволяют с помощью процедурной генерации решать такие задачи, как процедурная разрушаемость и прочее.
Помимо геймдева триангуляции используются в сетях, компьютерном зрении, различных аналитических алгоритмах, а так же в каких задачах вычислительной геометрии, как объединение и исключение многоугольников друг из друга (что бывает полезно часто и в задачах возникающих при разработке игр)
Ear Clipping with Holes
Пожалуй, один из самых простых алгоритмов триангуляции. Даёт не лучшую сетку и обладает большой вычислительной сложностью О(n^2) в худшем случае.
Bowyer–Watson algorithm
Алгоритм, генерирующий триангуляцию Делоне по набору точек. В целом, как и у большинства алгоритмов Делоне при правильной реализации алгоритмическая сложность O( n log n), где n – количество вершин. Но в некоторых случаях может занимать O(n^2).
Минусами относительно Ear Clipping является то, что данный алгоритм строит выпуклую триангуляцию и в базовой реализации не предполагает дыр в получившейся сетке.
Обработка триангуляции Делоне (Delaunay refinement)
Chew’s second algorithm и Ruppert’s algorithm – это алгоритмы, которые позволяют вводить ограничения в триангуляцию Делоне и задать минимальный угол треугольника в сетке. Важной деталью алгоритмов является то, что они могут уйти в бесконечный цикл и гарантировано дают результат при углах между примерно 20.7 градусов и 29 градусов. Возможность задать минимальный угол важна при решении задач, описанных выше.
Chew’s second algorithm реализован в бесплатном пакете www.cs.cmu.edu/
Triangle.Net для Unity
Ну и раз уж с помощью триангуляций можно решать так много крутых задач, то на праздниках захотелось реализовать свою обёртку для Unity, чтобы всегда иметь под рукой удобный инструмент. В данной реализации алгоритм триангуляции в среднем работает за O(n), а в худшем за O(n * log n) – где n-количество вершин. К примеру, при тесте на 1кк вершин случайно разбросанных по квадрату юнити в редакторе на Intel Core i7-8700 строило сетку в среднем за 7.56 секунд.
Основные отличия от оригинальной библиотеки в наличии методов расширений заточенных под Unity, а так же замена double на float во всей библиотеке (+ пара определённых операторов для каста) Double в юнити не имеет особого смысла. Если считать физические симуляции, то я бы использовал отдельное приложение на плюсовой библиотеке, а результат вычислений уже отдавал Unity чисто для визуализации. А также переименован тип Mesh на TriangleNetMesh, чтобы не сбивать относительно Mesh из Unity. Да, они и так в разных неймспейсах, но тем не менее думаю новичков немного сбивал бы тот факт, что мы с помощью одного Mesh получаем другой.
Суть библиотеки в том, что вы сначала должны задать так называемый полигон. Потом на его основе сгенерировать уже меш. Для того, чтобы это работало с юнитёвыми структурами данных было введено несколько методов расширений.
Для демонстрации и примера использования была сделана специальная демо-сцена с возможностью отрисовки мешей. С ней и портом библиотеки можно ознакомится в github проекте.
Спасибо за внимание! Надеюсь, что порт и статья кому-то будут полезны и стало чуть понятнее, зачем нужны триангуляция и знание математики в целом. Буду стараться продолжать раскрывать применения и способы решения разных математических задач в геймдеве. В самой вычислительной геометрии ещё очень много интересного, но помимо ещё есть множество других интересных разделов высшей математики.
Алгоритм триангуляции Делоне методом заметающей прямой
Доброго времени суток!
В этой статье я подробно опишу алгоритм, который у меня получился в результате использования идеи «заметающей прямой» для построения триангуляции Делоне на плоскости. В нем есть несколько идей, которые я нигде не встречал, когда читал статьи про триангуляцию.
Возможно, кто-то тоже найдет их необычными. Я постараюсь сделать все в лучших традициях и включить в рассказ следующие вещи: описание используемых структур данных, описание шагов алгоритма, доказательство корректности, временные оценки, а также сравнение с итеративным алгоритмом, использующим kD-дерево.
Определения и постановка задачи
Триангуляция
Говорят, что на множестве точек на плоскости задана триангуляция, если некоторые пары точек соединены ребром, любая конечная грань в получившемся графе образует треугольник, ребра не пересекаются, и граф максимален по количеству ребер.
Триангуляция Делоне
Триангуляцией Делоне называется такая триангуляция, в которой для любого треугольника верно, что внутри описанной около него окружности не находится точек из исходного множества.
Замечание: для заданного множества точек, в котором никакие 4 точки не находятся на одной окружности, существует ровно одна триангуляция Делоне.
Условие Делоне
Пусть на множестве точек задана триангуляция. Будем говорить, что некоторое подмножество точек удовлетворяет условию Делоне, если триангуляция, ограниченная на это подмножество, является триангуляцией Делоне для него.
Критерий для триангуляции Делоне
Выполнение условия Делоне для всех точек, образующих четырехугольник в триангуляции, эквивалентно тому, что данная триангуляция является триангуляцией Делоне.
Замечание: для невыпуклых четырехугольников условие Делоне всегда выполнено, а для выпуклых четырехугольников (вершины которого не лежат на одной окружности) существует ровно 2 возможные триангуляции (одна из которых является триангуляцией Делоне).
Задача заключается в том, чтобы для заданного множества точек построить триангуляцию Делоне.
Описание алгоритма
Видимые точки и видимые ребра
Пусть задана минимальная выпуклая оболочка (далее МВО) конечного множества точек (ребра, соединяющие некоторые из точек так, чтобы они образовывали многоугольник, содержащий все точки множества) и точка A, лежащая вне оболочки. Тогда точка плоскости называется видимой для точки А, если отрезок, соединяющий ее с точкой А, не пересекает МВО.
Ребро МВО называется видимым для точки А, если его концы видимы для А.
На следующей картинке красным помечены ребра, видимые для красной точки:
Замечание: контур триангуляции Делоне является МВО для точек, на которых построена.
Замечание 2: в алгоритме видимые для добавляемой точки А ребра образуют цепочку, то есть несколько подряд идущих ребер МВО
Хранение триангуляции в памяти
Есть некоторые стандартные способы, неплохо описанные в книге Скворцова [1]. Ввиду специфики алгоритма, я предложу свой вариант. Так как хочется проверять 4-угольники на условие Делоне, то рассмотрим их строение. Каждый 4-угольник в триангуляции представляет из себя 2 треугольника, имеющих общее ребро. У каждого ребра есть ровно 2 треугольника, прилегающих к нему. Таким образом, каждый четырехугольник в триангуляции порождается ребром и двумя вершинами, находящимися напротив ребра в прилегающих треугольниках.
Так как по ребру и двум вершинам восстанавливаются два треугольника и их смежность, то по всем таким структурам мы сможем восстановить триангуляцию. Соответственно предлагается хранить ребро с двумя вершинами в множестве и выполнять поиск по ребру (упорядоченной паре вершин).
Алгоритм
Идея заметающей прямой заключается в том, что все точки сортируются по одному направлению, а затем по очереди обрабатываются.
Проверка условия Делоне
Способы проверки четырехугольников на условие Делоне можно найти в той же книжке [1]. Подмечу лишь, что при выборе метода с тригонометрическими функциями оттуда при неаккуратной реализации могут получаться отрицательные значения синусов, есть смысл брать их по модулю.
Поиск видимых ребер
Осталось понять, как эффективно находить видимые ребра. Заметим, что предыдущая добавленная точка S находится в МВО на текущей итерации, так как имеет наибольшую координату , а также видима для текущей точки. Тогда, замечая, что концы видимых ребер образуют непрерывную цепочку видимых точек, мы можем идти от точки S в обе стороны по МВО и собирать ребра, пока они видимы (видимость ребра проверяется с помощью векторного произведения). Таким образом удобно хранить МВО как двусвязный список, на каждой итерации удаляя видимые ребра и добавляя 2 новых из рассматриваемой точки.
Визуализация работы алгоритма
Две красные точки — добавляемая и предыдущая. Красные ребра в каждый момент составляют стек рекурсии из шага (4):
Корректность алгоритма
Чтобы доказать корректность алгоритма, достаточно доказать сохранение инварианта в шагах (3) и (4).
Шаг (3)
После шага (3), очевидно, получится некоторая триангуляция текущего множества точек.
Шаг (4)
В процессе выполнения шага (4) все четырехугольники, не удовлетворяющие условию Делоне, находятся в стеке рекурсии (следует из описания), а значит, по окончании шага (4) все четырехугольники удовлетворяют условию Делоне, то есть действительно построена триангуляция Делоне. Тогда осталось доказать, что процесс в шаге (4) когда-нибудь закончится. Это следует из того, что все ребра, добавленные при перестроении, исходят из текущей рассматриваемой вершины (то есть на шаге их не больше, чем
) и из того, что после добавления этих ребер мы не будем рассматривать четырехугольники, порожденные ими (см. предыдущее замечание), а значит, добавим не более одного раза.
Временная сложность
В среднем на равномерном, нормальном распределениях алгоритм работает довольно неплохо (результаты приведены ниже в табличке). Есть предположение, что время его работы составляет . В худшем случае имеет место оценка
.
Давайте разберем время работы по частям и поймем, какая из них оказывает самое большое влияние на итоговое время:
Сортировка по направлению
Для сортировки будем использовать оценку .
Поиск видимых ребер
Для начала покажем, что время, суммарно затраченное на поиск видимых ребер, есть . Заметим, что на каждой итерации мы находим все видимые ребра и еще 2 (первые не видимые) за линейное время. В шаге (3) мы добавляем в МВО новые 2 ребра. Таким образом, всего в меняющейся на протяжении алгоритма МВО побывает не более
ребер, значит, и различных видимых ребер будет не более
. Еще мы найдем
ребер, не являющихся видимыми. Таким образом, в общей сложности найдется не более
ребер, что соответствует времени
.
Построение новых треугольников
Суммарное время на построение треугольников из шага (3) с уже найденными видимыми ребрами, очевидно, .
Перестроение триангуляции
Осталось разобраться с шагом (4). Сначала заметим, что проверка условия Делоне и перестроение в случае его не выполнения являются довольно дорогими действиями (хоть и работают за ). Только на проверку условия Делоне может уйти около 28 арифметических операций. Посмотрим на среднее количество перестроений в течение этого шага. Практические результаты на некоторых распределениях приведены ниже. По ним очень хочется сказать, что среднее количество перестроений растет с логарифмической скоростью, однако оставим это как лишь предположение.
Здесь еще хочется подметить, что от направления, вдоль которого производится сортировка, может сильно варьироваться среднее число перестроений на точку. Так на миллионе равномерно распределенных на длинном низком прямоугольнике с отношением сторон 100000:1 это число варьируется от 1.2 до 24 (эти значения достигаются при сортировке данных по горизонтали и вертикали соответственно). Поэтому я вижу смысл выбирать направление сортировки произвольным образом (в данном примере при произвольном выборе в среднем получалось около 2 перестроений) или выбрать его вручную, если данные заранее известны.
Таким образом, основное время работы программы обычно уходит на шаг (4). Если же он выполняется быстро, то есть смысл задуматься над ускорением сортировки.
Худший случай
В худшем случае на -ой итерации происходит
рекурсивный вызов в шаге (4), то есть, суммируя по всем i, получаем асимптотику в худшем случае
. Следующая картинка иллюстрирует красивый пример, на котором программа может работать долго (1100 перестроений в среднем при добавлении новой точки при входных данных в 10000 точек).
Сравнение с итеративным алгоритмом построения триангуляции Делоне с использованием kD-дерева
Описание итеративного алгоритма
Коротко опишу вышеуказанный алгоритм. При поступлении очередной точки мы с помощью kD-дерева (советую почитать про него где-нибудь, если вы не знаете) находим довольно близкий к ней уже построенный треугольник. Затем обходом в глубину ищем треугольник, в который попадает сама точка. Достраиваем ребра в вершины найденного треугольника и фактически выполняем шаг (4) из нашего алгоритма для новых четырехугольников. Так как точка может быть вне триангуляции, то для упрощения предлагается накрыть все точки большим треугольником (построить его заранее), это решит проблему.
Сходство алгоритмов
Различия алгоритмов
В итеративном алгоритме локализация точки (поиск нужного треугольника) происходит в среднем за , на вышеуказанных распределениях в среднем происходит 3 перестроения (как показано в [1]) при условии произвольного порядка подачи точек. Таким образом заметающая прямая выигрывает время у итеративного алгоритма в локализации, но проигрывает его в перестроениях (которые, напомню, довольно тяжелые). Ко всему прочему итеративный алгоритм работает в режиме онлайн, что также является его отличительной особенностью.
Заключение
Здесь я просто покажу некоторые интересные триангуляции, получившиеся в результате работы алгоритма.
Триангуляция что это в математике
Такая задача возникает, например, при построении графиков функций, генерации сложных ландшафтов.
Возможное решение этой проблемы состоит в дискретизации: для всех точек не из A пусть f(p) равняется высоте ближайшей точки из A. Получится нечто вроде нарисованного ниже.
Выглядит довольно неестественно. Гораздо лучше воспользоваться триангуляцией данного набора точек, а потом поднять каждую точку на высоту, соответствующую ее положению в треугольнике.
Формальное определение триангуляции: планарный граф, получающийся при соединении точек A отрезками, такой, что нельзя добавить ни одного нового отрезка без нарушения планарности (т.е без пересечения отрезками друг друга). При этом граница триангуляции будет, очевидно, выпуклой оболочкой множества A.
Одно и то же множество можно триангулировать разными способами. Рассмотрим углы в получающихся треугольниках. Выберем минимальный угол. Триангуляция дает тем лучшую аппроксимацию, чем больше ее минимальный угол, при этом формируемые треугольники ‘стремятся к равноугольности’. Особенно важна максимизация минимального угла в задачах вычислительной математики, когда точность производимых вычислений очень сильно зависит от размера минимального угла триангуляции графика. Наилучшей в этом смысле триангуляцией является триангуляция Делоне.
Простейшим способом ее построения является инкрементальный алгоритм, работающий за O(n 2 ) операций. Реализация из соответствующей статьи не поддерживает вырожденные случаи, когда 4 точки из множества лежат на одной окружности: в этом случае триангуляция Делоне не уникальна, их несколько, но минимальные углы этих триангуляций равны.
Обычно рассматривают триангуляцию на плоскости, однако триангуляция Делоне аналогично определяется и для n-мерного пространства.
Диаграмма Вороного
Рассмотрим следующую задачу: есть почтовые службы pi, мы хотим знать, какую область плоскости обслуживает каждая. При этом каждую точку q обслуживает та служба, которая ближе. Ответ на этот и ряд других вопросов, связанных с близостью на плоскости, дает диаграмма Вороного.
Диаграмма Вороного изображена на рисунке выше. Она состоит из вершин(v) диаграммы и ее сторон(e). Формальное определение:
Можно провести очень интересную аналогию из области кристаллографии. Предположим, что точки представляются в виде зерен кристалла, которые растут с постоянной скоростью во всех направлениях. Предположим также, что рост зерен кристалла продолжается до тех пор, пока два или более зерен не встретятся. Через некоторое достаточное время каждое выросшее зерно будет представлено в виде ячейки диаграммы Вороного для своего ядра(вполне очевидно, что рост крайних неограниченных областей может продолжаться бесконечно). В результате будет получена диаграмма Вороного для множества P.
Весьма забавно представить более быстрый процесс роста в одном из направлений и при расположении ядер в фиксированных точках в пространстве. Полученная в результате диаграмма Вороного в трехмерном пространстве будет состоять из ограниченных и неограниченных полиэдральных областей.
Диаграмма Вороного аналогично определяется для n-мерного пространства. Алгоритм построения диаграммы Вороного описан в англоязычной статье.
Связь между диаграммой Вороного и триангуляцией Делоне
Так что можно говорить об однозначном соответствии между диаграммой Вороного и триангуляциями Делоне. Посмотрим, например, как из диаграммы получается триангуляция.
Таким образом, триангуляция получается соединением всех смежных точек P отрезками. Единственная проблема: четыре или более точек в графе Делоне могут лежать на одной окружности.
Такие точки образуют выпуклые полигоны, которые триангулируются добавлением произвольных непересекающихся диагоналей. Тогда триангуляция будет окончательной. Можно сказать, что триангуляция Делоне получается из графа Делоне добавлением 0 или более ребер.
Связь выпуклой оболочки с триангуляцией Делоне и диаграммой Вороного
N-мерная триангуляция Делоне и диаграмма Вороного могут быть получены путем вычисления N+1-мерной выпуклой оболочки. Процесс довольно простой, но выходит за рамки общего раздела, а потому описан в отдельной статье. Там же описано и получение выпуклой оболочки на плоскости из диаграммы.
Информацию о решении конкретных задач можно также найти в разделе Олимпиадные задачи: геометрия.