ПрограммированиеФорумФизика

Джойнты и степени свободы для самых маленьких (2 стр)

Страницы: 1 2 3 4 5 Следующая »
#15
10:39, 12 июля 2025

Рано я обрадовался, что все сделал. При попытке сделать подобие веревки, получил такой вот расколбас:

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

На этом мысль остановилась. Что у меня может быть напутано, судя по такому эффекту?

#16
12:28, 12 июля 2025

Подобрался немного к решению.
На видео видно, что когда нормаль контакта начинает совпадать по направлению с вектором "центр модели - точка склеивания", такое впечатление что модель как бы "переворачивается" на 180 градусов и начинается расколбас. На видео псевдоскорости выключены:

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

На рисунке, в таком положении солвер у меня пытается развернуть на 180 градусов обе модели:

Изображение

#17
17:07, 12 июля 2025

texture3D
> Решаем этот контакт примерно также как и контакт нормали. Здесь нормалью при решении будет выступать не нормаль какого-то из тел, а нормализованный вектор между глобальными координатами заданной точки контакта 1 тела и глобальными координатами точки контакта 2 тела. А точкой контакта при решении будет выступать точка в глобальных координатах на полпути между этими заданными двумя точками.
Возник вопрос, у меня подозрение что я могу вектор для угловых скоростей считать неправильно.
Если красные точки - это точки склеивания, зеленые точки - центры тел, то зеленые линии - нужные нам векторы для подсчета угловых скоростей, верно?

Изображение
#18
18:00, 12 июля 2025

Если синяя точка - середина красного отрезка [p1;p2], то она будет точкой контакта p, относительно которой будем рассчитывать импульс и к ней его прикладывать к обоим телам.

  
  p = p1 + 0.5f * ( p2 - p1 ); // точка контакта

  ContNormal = ( p2 - p1 ).Normalize(); // нормаль контакта
  
  Depth = ( p2 - p1 ).Len(); // глубина проникновения

  Depth *= SomeFactor; // константа, которую подобрал на глаз

  n1 = ContNormal;
  n2 = -ContNormal;

  w1_1 = VecMul( ContNormal, p - Obj1.Pos );
  w2_1 = VecMul( ContNormal, Obj2.Pos - p );

  a = n1 * Obj1.Vel +
    w1_1 * Obj1.AngVel +

    n2 * Obj2.Vel +
    w2_1 * Obj2.AngVel;

  a -= Depth;   // здесь учитывается глубина растяжения/сжатия пружины
                // чем дальше разлетелись точки, тем сильнее будет импульс

  if ( a >= 0.0f )
  {
      return;
  }


  wwwx1 = w1_1 * Obj1.AxisX;
  wx1 = wwwx1;
  wwwx1 *= Obj1.vInvInertia.x;
  wx1 *= wwwx1;

  wwwy1 = w1_1 * Obj1.AxisY;
  wy1 = wwwy1;
  wwwy1 *= Obj1.vInvInertia.y;
  wy1 *= wwwy1;

  wwwz1 = w1_1 * Obj1.AxisZ;
  wz1 = wwwz1;
  wwwz1 *= Obj1.vInvInertia.z;
  wz1 *= wwwz1;
      
  wwwx2 = w2_1 * Obj2.AxisX;
  wx2 = wwwx2;
  wwwx2 *= Obj2.vInvInertia.x;
  wx2 *= wwwx2;

  wwwy2 = w2_1 * Obj2.AxisY;
  wy2 = wwwy2;
  wwwy2 *= Obj2.vInvInertia.y;
  wy2 *= wwwy2;

  wwwz2 = w2_1 * Obj2.AxisZ;
  wz2 = wwwz2;
  wwwz2 *= Obj2.vInvInertia.z;
  wz2 *= wwwz2;

  b = Obj1.InvMass + Obj2.InvMass +
    wx1 + wx2 + wy1 + wy2 + wz1 + wz2;
    
  nImpulse = -a / b;


  if ( ! Obj1.IsStatic )
  {
    Obj1.Vel += ( nImpulse * Obj1.InvMass * ContNormal );

    Obj1.AngVel += ( Obj1.AxisX * ( nImpulse * wwwx1 ) );
    Obj1.AngVel += ( Obj1.AxisY * ( nImpulse * wwwy1 ) );
    Obj1.AngVel += ( Obj1.AxisZ * ( nImpulse * wwwz1 ) );
  }


  if ( ! Obj2.IsStatic )
  {
    Obj2.Vel -= ( nImpulse * Obj2.InvMass * ContNormal );
      
    Obj2.AngVel += ( Obj2.AxisX * ( nImpulse * wwwx2 ) );
    Obj2.AngVel += ( Obj2.AxisY * ( nImpulse * wwwy2 ) );
    Obj2.AngVel += ( Obj2.AxisZ * ( nImpulse * wwwz2 ) );
  }
#19
19:08, 12 июля 2025

texture3D
> p = p1 + 0.5f * ( p2 - p1 ); // точка контакта

p = .5f * (p1 + p2);

#20
5:00, 13 июля 2025

texture3D
> Если синяя точка - середина красного отрезка [p1;p2], то она будет точкой контакта p, относительно которой будем рассчитывать импульс и к ней его прикладывать к обоим телам.
У меня сделано по такому же принципу, только написано немного по другому.
Явно же проблема с угловыми скоростями, которая почему проявляется когда направление нормали близко к (p - Obj1.Pos) - тогда даже видно, что импульсы считаются неправильно и начинается разлет. Если направление сильно различается, то проблемы нет, это больше всего смущает. Ты не сталкивался с подобным во время разработки? На видео псевдоскорости отключены для того, чтобы баг был виден лучше.

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

Ладно, буду копать почему так.

#21
6:20, 13 июля 2025

AMM1AK
> p = .5f * (p1 + p2);

В вещественной арифметике
p1 + 0.5f * ( p2 - p1 )
и
.5f * (p1 + p2)
дадут разный результат, если p1, p2 очень большие или наоборот очень близкие у нулю или просто p1 и p2 очень близки друг к другу. 

Например, если прибавить к какому-то X много раз очень близкое к нулю число, то можно получить это же X.

Как-то давно при примерно такой же оптимизации заметил, что при замене

x = a * b + a * c + a * d + a * e
на
x = a * ( b + c + d + e)
решение начинает сходиться медленнее из-за того, что результаты верхнего и нижнего выражения дают немного разный результат и пришлось оставить верхнее.
Поэтому, некоторые оптимизации являются не оптимизациями, а скрытыми источниками проблем.

#22
6:29, 13 июля 2025

MikeNew
>проявляется когда направление нормали близко к (p - Obj1.Pos) - тогда даже видно, что импульсы считаются неправильно и начинается разлет


Вообще что-то такое было, но обычно не в случае всего двух тел.

Например, если задать контакт между первым и вторым телом, который сам по себе отрабатывает нормально.
Но при этом заданы еще какие-то контакты (обычно с каким-то третьим телом), которые не могут быть решены одновременно с первым

Изображение

То есть здесь либо решаем только контакт 1 p1p2 либо контакт 2 p1p2, вместе они решены быть не могут и будет дёрганье.


Еще есть предположение что неправильно вычисляются глобальные координаты p1,p2.


Я бы попробовал всё максимально упростить.
Оставить только 2 тела, убрать псевдоскорости .
Оставить только один контакт между этими телами.
и задать нулевые координаты обоим точкам контакта.

То есть чтобы точка первого тела располагалась в его центре, а вторая точка - в центре второго тела.

+ Показать

Тела при этом могут вращаться друг относительно друга, но хотя бы глюков быть не должно.
Если дёрганье пропадёт, то тогда можно первую точку поставить между телами (как сейчас), вторая пусть также будет с нулевыми координатами в центре второго тела.


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

Если они очень близко друг к другу, то и вовсе можно их не применять.
Что-то вроде

Depth -= 0.01f;

if ( Depth < 0.01f )
{
  Depth = 0.0f;
}

if ( Depth > 0.1f )
{
  Depth = 0.1f;
}


По сути, здесь ведь точно такое же решение контакта, какие у тебя уже и так есть(нормали и трения). Другие только нормаль контакта и точка приложения импульса.

PS А warmstarting при их решении не используешь? Просто вспомнил, что с ним у меня тоже эта часть глючила.

И порядок решений контактов у меня был условно такой:

for ( i = 0; i < ContactsNum; i ++)
{
  Contacts[ i ].SolveNormalContact();
  Contacts[ i ].SolveFriction1Contact();
  Contacts[ i ].SolveFriction2Contact();
}

и только потом

for ( i = 0; i < GlueContactsNum; i ++)
{
  GlueContacts[ i ].Solve();
}

#23
9:20, 13 июля 2025

texture3D
Во дела.. 😃

#24
10:06, 13 июля 2025

texture3D
> Тела при этом могут вращаться друг относительно друга, но хотя бы глюков быть не должно.
Так и есть, относительно друг друга вращались, но глюков не было.
texture3D
> Если дёрганье пропадёт, то тогда можно первую точку поставить между телами (как сейчас), вторая пусть также будет с нулевыми координатами в центре второго тела.
Первое тело начало снова дергать, как и ожидалось. Что-то не то у меня с угловыми скоростями, определенно, может все-таки солвер в чем-то кривой.
Кстати, на видео видно, что второе тело закрутило вокруг оси Y, это нормально? Оно же вовсе не должно вот так вот вращаться из-за контакта при точке контакта совпадающей с центром масс, или это допуcтимо?

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

texture3D
> Еще есть предположение что неправильно вычисляются глобальные координаты p1,p2.
Они на видео сферами отображены чтобы их видеть, считаются максимально просто - позиция тела плюс матрица вращения тела умножить на локальные координаты контакта. Тут точно ошибки нет.

texture3D
> А warmstarting при их решении не используешь?
Не было с самого начала для этого типа контактов.

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

texture3D
> И псевдоскорости лучше ограничивать.
Пока что вовсе отключены, до победы над расколбасом.

#25
10:42, 13 июля 2025

MikeNew
> Кстати, на видео видно, что второе тело закрутило вокруг оси Y, это нормально? Оно же вовсе не должно вот так вот вращаться из-за контакта при точке контакта совпадающей с центром масс, или это допуcтимо?

Чисто теоретически - не должно.

Из кода в #18

(считаем что таскаем второе тело, а крутится вокруг оси первое)

  p = p1 + 0.5f * ( p2 - p1 ); // точка контакта
  ContNormal = ( p2 - p1 ).Normalize(); // нормаль контакта
  ...
  w1_1 = VecMul( ContNormal, p - Obj1.Pos );

Здесь w1_1 будет равны нулю потому что СontNormal и точка контакта лежат на одной линии между центрами масс.

А значит и

  wwwx1 = w1_1 * Obj1.AxisX;
  wx1 = wwwx1;
  wwwx1 *= Obj1.vInvInertia.x;
  wx1 *= wwwx1;

  wwwy1 = w1_1 * Obj1.AxisY;
  wy1 = wwwy1;
  wwwy1 *= Obj1.vInvInertia.y;
  wy1 *= wwwy1;

  wwwz1 = w1_1 * Obj1.AxisZ;
  wz1 = wwwz1;
  wwwz1 *= Obj1.vInvInertia.z;
  wz1 *= wwwz1;

wwwx1, wwwy1, wwwz1 тоже будут равны нулю

и соответственно, к угловой скокрости добавим нули

if ( ! Obj1.IsStatic )
{
    Obj1.Vel += ( nImpulse * Obj1.InvMass * ContNormal );

    Obj1.AngVel += ( Obj1.AxisX * ( nImpulse * wwwx1 ) );
    Obj1.AngVel += ( Obj1.AxisY * ( nImpulse * wwwy1 ) );
    Obj1.AngVel += ( Obj1.AxisZ * ( nImpulse * wwwz1 ) );
}

Но это при полном отсутствии других воздействий.

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

#26
11:00, 13 июля 2025
Изображение

Изображение

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

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

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

#27
11:14, 13 июля 2025
Изображение

Вот в 16 посте на 7 секундах точки p1 и p2 уезжают куда-то не туда. Относительно своих тел они не должны сдвигаться вообще.

Эти точки должны пересчитываться сразу после интегрирования позиций/поворотов тел.

#28
16:45, 13 июля 2025

texture3D
> Здесь w1_1 будет равны нулю потому что СontNormal и точка контакта лежат на одной линии между центрами масс.
Проверил самый простой случай, когда у обоих тел точка контакта совпадает с центром масс.
w1_1 и все остальное считаю как у тебя в посте 18.

w1_1 и w2_1 должны быть равными нулю (потому что СontNormal и точка контакта лежат на одной линии между центрами масс, как ты сказал), а получаются значения на манер:

w2_1.x = 0.000000
w2_1.y = -0.000036
w2_1.z = 0.000000

Поэтому тела закручивает вокруг Y-оси.
Похоже на проблемы точности float или на кривую работу glm::normalize

В связи с этим вопрос - а как у тебя происходит нормализация вектора нормали? Самописная функция или из какой-то библиотеки?
Используешь float или может double?

#29
16:48, 13 июля 2025

texture3D
> Заметил на видео, точка у левого тела, (которое таскаем) постоянно скачет, а должна быть там где красная.
У меня дебажные сферы и линии отстают от рендера, примерно как программный курсор отстает от аппаратного. Но надо перепроверить, для полной уверенности.

Страницы: 1 2 3 4 5 Следующая »
ПрограммированиеФорумФизика

Тема в архиве.