o2 dev
108 subscribers
49 photos
4 videos
25 files
54 links
About o2 engine development
加入频道
Есть специальные техники для борьбы с такими эффектами, называется continuous collision detection. При проверке пересечения тел используется не только их текущее положение, но и положение на прошлом кадре. То есть для той самой пули будет учтено, что она была с другой стороны стены и теперь она не пролетит сквозь нее
Но базово это работает всегда так - раз в какое-то время все замирает, обсчитывается и 1/60 секунды летит как получилось.
Теперь, какие подходы в физике игр вообще бывают. Их есть целых две штуки:
- физика твердых тел
- физика мягких тел (или иногда называют физика верле)

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

Как следует из названия, в таком подходе эмулируется поведение абсолютно твердых тел. Тело описывается некой геометрией, описывающей ее объем, массой и инерцией. Эта форма не может измениться. Тела могут сталкиваться друг с другом, отскакивать друг от друга и между ними существует трение

Начнем с общего описания твердого тела (rigid body):
- трансформация. Матрица (или вектор + кватернион) описывающие положение и поворот тела в пространстве
- масса. Сколько весит тело в килограммах
- линейная и угловая скорости. Два вектора, показывающие в какую сторону и скакой скоростью летит/крутится тело
- инерция тела / момент инерции. Параметры, показывающий насколько сильно тело сопротивляется внешней силе, пытающейся его сдвинуть с места или закрутить

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

Есть и другие силы, действующие на тела - гравитация, соединения тел (joints), и другие силы, которые можно задать через движок ради каких-то эффектов, напр. аэродинамика
У тел есть форма, оболочка, или коллайдер. Она может состоять из одной или нескольких геометрий. Эта геометрия не меняется, что и соответствует определению физики тверого тела. С помощью коллайдеров движок понимает пересеклись ли тела и нужно ли рассчитать импульс взаимодействия
И это довольно глубокая тема, и может быть даже наиболее сложная в физическом движке. Дело в том, что тел может быть ооочень много, например - тысяча. Обычно меньше, но тем не менее под сотню - это норма. И нужно проверить не пересекается ли каждое тело с каждый другим. Итого даже для 100 тел это будет неимоверное количество проверок (факториал от ста?). Да и сама проверка тоже не легкая. Просто попробуйте представить алгоритм для проверки пересечения двух параллелепипедов. Они могут быть под разным углом друг к другу. Более того, нужно найти точки, в которых происходит пересечение, и еще ко всему этому как-то идентифицировать одни и те же точки соприкосновения от кадра к кадру, но об этом позже

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

Какие есть подходы к широкой фазе? Самый примитивный - bounding box. Каждое тело описывается крайне простой оболочкой, которую легко проверять на пересечение, и в которую полностью вписывается реальная. более меньшая и точная геометрия. Для таких целей подходит axis aligned bounding box (AABB) или сферы. AABB - это прямоугольник, выровненный по осям. То есть для проверки пересечения двух AABB применяются довольно простые формулы:
- if (one.right < second.left one.left > second.right one.top < second.bottom || one.bottom > second.top) return false;

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

Так же есть методы разбиения пространства: quad tree или сортировка по одной из осей. В общем, все эти хитрости направлены на то, чтобы поменьше проверок попало в узкую фазу, где начинается настоящее рубилово
И это чистая правда. Здесь начинается настоящая векторная математика, оптимизации и куча интересностей. Нам надо получить примерно такую картину:
Зайдем с самого очевидного и самого сложного кейса - геометрия описывается мешем, то есть набором треугольников. С одной стороны тут все просто - проверить все пары треугольников на пересечения 2х треугольников. Но тут же всплывают нюансы: треугольников много (та же проблема с количеством пар проверки), у треугольников есть общие ребра и часть точек нужно мержить. А еще не всегда понятно в какую сторону делать нормаль, проверки треугольников запросто могут противоречить друг другу, от этого зависит в какую сторону выталкивать тела. В конечном итоге проверка мешей - это самый сложный и тяжелый вариант для обсчета физики. К тому же наименее стабильный

Гораздо проще происходит дело с простыми телами, а именно выпуклыми. Это может быть любая форма с любым количеством вершин, но ни одна часть не может быть впуклой. В чем преимущество? Мы явно можем определить ось, разделяющие тела. Нормаль коллизии всегда будет направлена вдоль этой оси, что дает стабильность. Более подробно об этом методе - separating axis theorem

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

Решением контактов занимается солвер (solver), дословно решатель. Его задача разрешить все ограничения взаимодействия тел и джонтов. Это является одной из стадий работы физического движка:
- интеграция тел (их движение в течение 1/60 секунды)
- поиск столкновений: широкая фаза и узкая
- решение ограничений (контактов и соединений)
- применение других сил (гравитация, игровые механики)
- повторяем
Для каждого контакта мы должны посчитать суммарный импульс двух тел и применить его обратно к телам. Все просто. Вроде бы
Контакт может быть только между парой тел. У тел, валяющихся в куче, куча контактов с соседними телами. Они обмениваются импульсами, и в итоге эта куча как-то себя ведет - лежит, разваливается и тп. В общем тела взаимодествуют сугубо в парах
Для каждой контактирующей пары мы уже вычислили точки соприкосновения, нормаль и глубину. Суммарный импульс складывается из массы тел и их скоростей. Импульс считается относительно точки контакта, учитывается угловое вращение. Представьте себе палку, которая быстро крутится в воздухе и удаляется концом обо что-то. Импульст такого удара явно зависит от скорости вращения, ведь центр может быть недвижим. Формул не будет, это скучно, и они есть в статьях выше. А здесь у нас легкое чтиво

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

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

Плюс начинает проявляться эффект дрожания (jittering) - тела начинают дрожать, тк за 1/60 успевают перелететь нужно положение, из-за чего считается слишком большой импульс, что приводит снова к перелету в другую сторону, в итоге система не стабильна и вот так вот дрожит. Или вовсе взрывается, когда за 2-3 кадра импульсы нарастают в геометрической прогрессии и тела улетают в nan

Если вы вспомните старые игры, до массового распространения движков типа havok, physX, то вы вспомните все эти эффекты - физика в играх была либо любо глючная, либо никакая. Все из-за вот этой проблемы дискретности и применения импульсов. Это было еще где-то в начале 2000х годов. Но потом пришел Эрин Катто
это чувак, который написал Box2D. Забавно, ведь это изначально была просто демка на GDC (Game Dev Conference), где она показывал свой подход к решению проблемы. Кстати вот его канал на ютубе.

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

Эрин Катто придумал применил простой способ - считать все контакты в несколько итераций. То есть не один раз, а раз так 6-8 за кадр. При этом тела все еще не движутся, просто на каждой итерации импульсы в контактах уточняются и приближаются к более правильному решению системы. Собственно, так и работают разные численные методы - вместо решения системы уравнений аналитически, мы решаем их итерационно, приближая ответ к верному все ближе и ближе.

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

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

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