碰撞响应
两刚体发生碰撞后,基于守恒律解决碰撞后的状态。
算法
碰撞点
刚体和刚体在点处发生碰撞,有:
、为刚体的位置,、为碰撞点在刚体的局部坐标。
、为刚体的速度,、为刚体的角速度,、为碰撞点的速度。
为碰撞相对速度,为碰撞法向速度,为碰撞切线速度。
为碰撞后的法向速度,为恢复系数,1为完全弹性碰撞,0为完全粘性碰撞。
为碰撞后的切线速度,为摩擦系数,0为无摩擦碰撞,摩擦最大为把切线速度减至0。
为碰撞点处速度的改变量
摩擦
根据Coulomb friction,摩擦力的大小与正压力的大小成正比:
刚体冲量
为了在碰撞点处产生的改变量,需要在碰撞点处施加冲量。
假设在碰撞点处施加冲量,则刚体的状态变化有:
碰撞点处的速度变化:
用负的斜对称矩阵来代替叉乘:
两个刚体碰撞,由守恒律知施加在刚体a上的冲量与b上的大小相等,方向相反:
所以:
程序
void ForceBasedDynamics::collisionResponseByImpulse(CranePhysics::Rigidbody &rba, CranePhysics::Rigidbody &rbb,
Eigen::Vector3f n, Eigen::Vector3f x, float cor, float cof)
{
n = -n;
Vector3f xa = x - rba.GetPosition();
Vector3f xb = x - rbb.GetPosition();
Vector3f va = rba.GetVelocity() + rba.GetAngularVelocity().cross(xa);
Vector3f vb = rbb.GetVelocity() + rbb.GetAngularVelocity().cross(xb);
Vector3f v_rel = va - vb;
float v_rel_f = n.dot(va - vb);
Vector3f v_n_rel = v_rel_f * n;
if (v_rel_f > 0.f)
{
return;
}
Vector3f v_n_plus = -cor * v_n_rel;
Vector3f v_t_minus = v_rel - v_n_rel;
Vector3f t = v_t_minus.normalized();
float a = std::max(1.f - cof * (1.f + cor) * v_n_rel.norm() / v_t_minus.norm(), 0.f);
Vector3f v_t_plus = a * v_t_minus;
Vector3f delta_v = v_n_plus + v_t_plus - v_rel;
Matrix3f K = rba.GetMassInv() * Matrix3f::Identity() + rbb.GetMassInv() * Matrix3f::Identity() -
((-1.f * (xa).asSkewSymmetric()) * rba.GetInertiaMomentInv() * (-1.f * (xa).asSkewSymmetric()) +
(-1.f * (xb).asSkewSymmetric()) * rbb.GetInertiaMomentInv() * (-1.f * (xb).asSkewSymmetric()));
Vector3f J = K.inverse() * delta_v;
rba.SetVelocity(rba.GetVelocity() + rba.GetMassInv() * J);
rba.SetAngularVelocity(rba.GetAngularVelocity() + rba.GetInertiaMomentInv() * (xa.cross(J)));
rbb.SetVelocity(rbb.GetVelocity() + rbb.GetMassInv() * -J);
rbb.SetAngularVelocity(rbb.GetAngularVelocity() + rbb.GetInertiaMomentInv() * (xb.cross(-J)));
/*
if (v_t_minus.norm() > 0.F)
{
{
float fn_a = (-rba.force_ * rba.GetMassInv()).dot(-n);
float a1 = std::max(1.f - cof * fn_a * dt / v_t_minus.norm(), 0.f);
float a2 = std::min(a1, 1.f);
Vector3f va = rba.GetVelocity();
Vector3f vna = va.dot(n) * n;
Vector3f vta = va - vna;
Vector3f vta_plus = vta * a2;
Vector3f va_plus = vta_plus + vna;
rba.SetVelocity(va_plus);
}
{
float fn_b = (-rbb.force_ * rbb.GetMassInv()).dot(n);
float a1 = std::max(1.f - cof * fn_b * dt / v_t_minus.norm(), 0.f);
float a2 = std::min(a1, 1.f);
Vector3f vb = rbb.GetVelocity();
Vector3f vnb = vb.dot(n) * n;
Vector3f vtb = vb - vnb;
Vector3f vtb_plus = vtb * a2;
Vector3f vb_plus = vtb_plus + vnb;
rbb.SetVelocity(vb_plus);
}
}
*/
}
测试
参考
- 王华民-GAMES103-基于物理的计算机动画入门