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

И в этом огромны геморрой
Казалось бы 1/60 секунды - достаточно малый промежуток времени. Но не в физике... За это время может произойти многое. Как правило это связано с большими силами, и, как следствие, большими скоростями. Тела проникают слишком глубоко, или вовсе друг через друга. Классический пример: пуля, летящая через тонкую стенку. Один кадр она с одной стороны стены, следующий кадр - с другой. И никакого контакта между ними не было
Есть специальные техники для борьбы с такими эффектами, называется 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х годов. Но потом пришел Эрин Катто