Физика «на пальцах»: солверы физических движков (2 стр)
Автор: Александр Санников
Другие типы соединений (джойнтов)
Мы рассмотрели, как составляется ограничитель ((n1, w1, n2, w2), c) свободы для контакта и как он решается (нахождение lambda, применение импульса). Но контакт — далеко не единственный тип ограничения степеней свободы. К счастью, практика показала, что большинство других нужных в жизни типов ограничений степеней свободы можно описать точно такими же четырьмя векторами и одним дополнительным числом ((n1, w1, n2, w2), c). Как же определить, какие брать значения для каждого типа джойнта, который мы хотим задать? Опять же, вместо того, чтобы описывать общий не слишком простой механизм, проще попытаться понять на паре примеров:
1. Разработаем якобиан джойнта, который заставляет тела вращаться вокруг некоторой оси axis с одинаковой скоростью. Например, две шестерёнки одинакового радиуса. Такой джойнт будет решён, если сумма проекций угловых скоростей обоих тел на эту ось будет равна нулю, то есть:
j = ((0, axis, 0, -axis), 0);
2. Выведем якобиан для двух объектов, скользящих плоскостями друг по другу. Пример: плоская тарелка, скользящая по поверхности стола. Такой джойнт будет решён, если выполняются три условия:
- относительная скорость тарелки в проекции на нормаль стола равна нулю:
j = ((norm, 0, -norm, 0), 0)
- тарелка может вращаться только в плоскости стола. то есть две другие составляющие вектора их относительной угловой скорости равны нулю:
j = ((0, norm.GetPerpendicular1(), 0, -norm.GetPerpendicular1()), 0)
j = ((0, norm.GetPerpendicular2(), 0, -norm.GetPerpendicular2()), 0)
Такой джойнт может считаться решённым, если решены все три уравнения, то есть ограничены три степени свободы. Опять же, нам в SI совершенно не важно, сколько у нас уравнений в системе, мы просто решаем их все последовательно несколько раз.
О том, как составить уравнения на псевдоскорости для этих джойнтов, рекомендую подумать самостоятельно. Опять же, действуем по аналогии с джойнтом контакта.
Трение
В случае, если мы хотим моделировать бесконечное трение(проскальзывание между телами отсутствует), то его можно реализовать, добавив ещё две строки в якобиан, запрещающие точке контакта тел двигаться в двух направлениях, перпендикулярных друг другу и нормали контакта. То есть добавляем ещё два ограничителя степени свободы с параметрами:
((fdir1, w1, -fdir1, w2), 0)
((fdir2, w1, -fdir2, w2), 0)
Где fdir1 и fdir2 - направления, в которых действуют силы трения. Желательно, чтобы эти направления как можно меньше менялись при передвижении тел, но необходимо, чтобы они оставались перпендикулярны друг другу и нормали контакта. В общем случае эта задача решения не имеет - всегда найдётся такой вектор нормали, при небольшом смещении которого fdir1 и fdir2 значительно поменяются. Доказательство сводится к к теореме о причёсывании ежа. Тем не менее можно сделать, чтобы эти условия выполнялись практически всегда. Например, один из способов это сделать - получить fdir1 как векторное произведение нормали с произвольным постоянным вектором, а fdir2 - как произведение fdir1 и нормали. Тем не менее, этот способ не надёжен - если нормаль совпадает с выбранным произвольным вектором, то fdir1 получится нулевым. В качестве альтернативы можно хранить два произвольных вектора и считать произведение нормали с тем из них, который "менее параллелен". В общем случае fdir1 и fdir2 не обязательно должны быть нормированны, но для простоты лучше их нормализовать - хуже не будет.
Если мы хотим моделировать сухое трение, то ситуация немного осложняется. Как известно по закону Гука, трение не может быть больше по величине, чем сила реакции опоры, умноженная на некоторый коэффициент. Для этого я рекомендую решать ограничители, соответствующие нормали контакта и двум направлениям трения, все разом. То есть стратегия следующая:
- полностью решаем ограничитель для нормали контакта
- находим лямбды, как если бы коэффициент трения(назовём его friction) был бы бесконечным, то есть просто решаем джойнты с вышесоставленными якобианами, не применяя найденные импульсы.
- аналогично тому, как мы не давали переданному в нормали импульсу быть меньше нуля, не даём корню из суммы квадратов импульсов трения быть больше, чем коэф. трения, умноженный на лямбду в нормальной составляющей.
- применяем модифицированные импульсы трения
В случае, если используем warmstarting, не забываем, что мы ограничиваем уже не лямбды, а накопленные импульсы. Импульсы в джойнтах трения копятся точно так же, как и все остальные. Ограничения на импульс, передаваемый в двухосевом трении - достаточно хитрая процедура и я бы советовал ей заняться тогда, когда весь остальной солвер уже готов, отлажен и хорошо работает.
Уравнения на псевдоскорости для трения решать не нужно из физических соображений - если какое-то тело по другому таки проскользило, ну и бог с ним, возвращать его обратно смысла нет.
В реальности направление, в котром действует сила трения на точку, строго определено, почему же мы используем два направления? Как показала практика, можно моделировать и одноосевое трение, где направление действия силы трения направлено обратно проекции относительной скорости на плоскость контакта, именно такой подход обычно напрашивается из интуитивных соображений на первый взгляд. Но, к сожалению, во время решения LCP, точки касания тел могут свободно наращивать свою скорость в направлении, перпендикулярном этому выбранному, что в результате приведёт к нестабильностям.
Заключение
Вот, в принципе, и все знания, достаточные для написания собственного не слишком сложного солвера движка игровой физики. За гранью моих возможностей осталось освещение двух достаточно важных, но, вообще говоря, не слишком тривиальных вопросов: ассиметричный тензор инерции и составление уравнений на псевдоскорости.
Код
Я всегда был твёрдо уверен, да и сейчас тоже, что к статьям по программированию не нужны примеры с кодом. Статья нужна для того, чтобы объяснить, как это работает и как это запрограммировать, а не чтобы своровать из неё код. Но волею судеб пришлось написать очень упрощённую версию своего физ.двига с открытым кодом, который достаточно близко следует логике статьи, плюс в нём реализовано несколько важных моментов, которые в статью не вошли. Поэтому я решил выложить его в открытый доступ.
Видео демонстрации демки:
Её исходный код(Есть зависимость от SFML, в архив включён проект и скомпиленный SFML для студии 2013. Должно быть очень легко скомпилить под любой другой IDE/операционкой):
https://drive.google.com/open?id=1d86rMNOpVt4EZNOGnvTW9DzO3VN-KMDM
#gauss-seidel, #pgs, #основы, #солверы
5 января 2010 (Обновление: 9 авг 2020)