В настоящее время я работаю над программой моделирования 3D твердого тела. В настоящее время мне удалось заставить твердые тела сталкиваться с полом и правильно подпрыгивать, используя импульс. Тем не менее, моя проблема в том, что как только они отскочили, они постоянно ускоряются, несмотря на использование вектора трения, чтобы попытаться замедлить их.
Это код, когда вы упали на землю
Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);
Rvector relativeVelocities = m_Bodies[i].Vel - floorVelocity;
fDirection.normalize();Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal))
/ (1/m_Bodies[i].mass + floorMass);
Rvector friction = fDirection*mu*gravity.length()*m_Bodies[i].mass;
Rvector collision_forces = Rvector(0,1,0)*impulse;
collision_forces += friction ;
m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);
Спасибо
Редактировать:
Вот код интеграции.
void RigidBodySimulation::eulerIntegration(float dTime)
{
Rvector newVel;
Rvector newPos;
Rvector zero(0.0, 0.0, 0.0);
Real one_over_mass;
Rvector accel;
for( unsigned int i = 0 ; i < m_Bodies.size(); i++)
{
one_over_mass = 1/m_Bodies[i].mass;
newVel = m_Bodies[i].Vel + m_Bodies[i].force*one_over_mass*dTime;
newPos = m_Bodies[i].Pos + m_Bodies[i].Vel*dTime;
accel = m_Bodies[i].force / m_Bodies[i].mass;
m_Bodies[i].acceleration = accel;
m_Bodies[i].newPos = newPos;
m_Bodies[i].Vel = newVel;
m_Bodies[i].Pos = newPos;
}
}
Я должен сказать, что это довольно ужасный кусок кода, который у вас там есть, и я делаю это более 10 лет. Вы должны получить базовый учебник по динамике (например, Хиббелер).
Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal))
/ (1/m_Bodies[i].mass + floorMass);
Это уравнение выглядит так, как будто вы пытаетесь рассчитать импульс восстановления от удара (хотя расчет неверен). Во-первых, вы должны понимать, что импульс — это не то же самое, что сила. Импульс — это интеграл силы за определенный промежуток времени. Во время удара вы можете предположить, что этот период времени действительно мал, и поэтому вы выполняете мгновенное изменение скорости. И именно поэтому вы должны были указать, что код интеграции не имеет ничего общего с вычислением столкновений, потому что он игнорируется в этот момент, или, по крайней мере, так должно быть, если вы делаете расчеты на основе импульсов. Вот как должен выглядеть фактический расчет:
Real momentum_before = Rvector::dotProduct(m_Bodies[i].Vel * m_Bodies[i].mass + floorVelocity * floorMass, floorNormal);
Real rel_vel_after = -e * Rvector::dotProduct(relativeVelocities,floorNormal);
// conservation of momentum in normal direction gives this:
Real body_vel_after = (momentum_before + floorMass * rel_vel_after) / (m_Bodies[i].mass + floorMass);
Real floor_vel_after = body_vel_after - rel_vel_after;
Что на самом деле упрощает одну строку следующим образом:
Real body_vel_after = ( (m_Bodies[i].mass - e * floorMass) * Rvector::dotProduct(m_Bodies[i].Vel, floorNormal)
+ (1.0 + e) * floorMass * Rvector::dotProduct(floorVelocity, floorNormal)
) / (m_Bodies[i].mass + floorMass);
Однако, если вы предполагаете, что пол имеет бесконечную массу (или намного больше, чем масса тела), то у вас просто будет:
Real body_rel_vel_after = -e * Rvector::dotProduct(relativeVelocities, floorNormal);
Real body_vel_after = Rvector::dotProduct(floorVelocity, floorNormal) + body_rel_vel_after;
Это так просто. Но, согласно этому предположению, у вас нет сохранения импульса. Но в любом случае импульс реституции от удара можно рассчитать как:
Real impulse = m_Bodies[i].mass * (body_vel_after - Rvector::dotProduct(m_Bodies[i].Vel, floorNormal));
Теперь, поскольку импульс реституции является интегралом нормальной силы в течение небольшого периода времени, импульс Из трения во время удара можно рассчитать по этому восстановительному удару. Сила трения равна «мю», умноженной на нормальную силу, т.е. |Ff| = mu * |Fn|
это также справедливо для импульса, т.е. |If| = mu * |In|
, Итак, вы можете вычислить это напрямую:
Real friction_impulse = mu * fabs(impulse);
Но это только величина импульса трения. Его направление противоположно относительной тангенциальной скорости, которая:
Rvector tangent_rel_vel = relativeVelocities - Rvector::dotProduct(relativeVelocities, floorNormal) * floorNormal;
И это направление:
Rvector dir_rel_vel = tangent_rel_vel;
dir_rel_vel.normalize();
(Обратите внимание, что мне нужно сохранить тангенциальную скорость без изменений, потому что она понадобится позже)
В этот момент вы можете вычислить тангенциальную скорость после удара следующим образом (опять-таки, в предположении, что пол бесконечной массы, в противном случае, он сложнее):
Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * friction_impulse / m_Bodies[i].mass;
Однако что, если импульс трения заставляет тангенциальную относительную скорость достигать нуля? Это проблема, потому что в приведенной выше формуле часть импульса трения может сместиться в обратном направлении относительно тангенциальной относительной скорости, что будет означать, что во время последней части удара сила трения фактически действует в направлении скорость (не хорошо). Максимум трения можно сделать, это остановить относительное движение. Итак, вам нужно проверить это условие:
Real tang_rel_vel_change = friction_impulse / mBodies[i].mass;
Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * tang_rel_vel_change;
if ( tang_rel_vel_change > tangent_rel_vel.length() )
tangent_rel_vel_after = Rvector(0.0, 0.0, 0.0); // stop relative motion.
На данный момент все, что вам нужно сделать, это объединить две конечные скорости:
m_Bodies[i].Vel = floorVelocity + tangent_rel_vel_after + body_rel_vel_after * floorNormal;
И это все, по крайней мере, для этой очень простой задачи (бесконечная масса пола). В действительности с этим импульсным подходом становится все труднее иметь дело, когда вы усложняете вещи: два объекта конечной массы, множественные объекты и фактическую динамику твердого тела (потому что вы просто делаете динамику частиц здесь). Импульсный подход редко встречается где-либо, кроме простых примеров школьного двора, когда шары подпрыгивают на полу. Кстати, вы не должны называть это симулятором «твердого тела», поскольку все, что вы на самом деле делаете с динамикой частиц (а трехмерная динамика твердого тела намного сложнее, чем эта). Кроме того, ваш закон об интеграции ужасен, но это совсем другая история.
Трение в обратном направлении, не так ли?
Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);
Это в направлении скорости. Затем вы умножаете это на некоторые константы, затем добавляете это к скорости (вместе с импульсом)
collision_forces += friction ;
m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);
Так что это будет способствовать увеличению скорости вдоль пола.
Есть еще несколько странных вещей:
Ваш импульсный термин имеет: (1/m_Bodies[i].mass + floorMass)
Это добавляет 1 / массу к массе. Не должно ли это быть (1/(m_Bodies[i].mass + floorMass))
И затем в интеграторе вы вычисляете ускорение по силе, но затем никогда не интегрируете его, вы также прикладываете силу непосредственно к скорости. Так что же acceleration
член для?