以下用于解决两个对象之间的碰撞的代码假定恢复为零。物体的inverse_inertia
表示为矩阵 (glm::mat4
)。
void apply_impulse(Body& body, glm::vec3 impulse, glm::vec3 offset)
{
body.velocity += impulse * body.inverse_mass;
body.angular_velocity += body.inverse_inertia * glm::cross(offset, impulse);
}
void resolve_collision(Body& a, Body& b, glm::vec3 contact_point, glm::vec3 normal)
{
glm::vec3 ra = contact_point - a.position;
glm::vec3 rb = contact_point - b.position;
glm::vec3 relative_velocity =
b.velocity + glm::cross(b.angular_velocity, rb) -
a.velocity - glm::cross(a.angular_velocity, ra);
// Not moving towards each other, ignore the collision as it will be resolved anyway
if (glm::dot(relative_velocity, normal) > 0) {
return;
}
normal = glm::normalize(normal);
float inverse_mass_sum = a.inverse_mass + b.inverse_mass +
glm::length2(a.inverse_inertia * glm::cross(ra, normal)) +
glm::length2(b.inverse_inertia * glm::cross(rb, normal));
float normal_impulse = -glm::dot(relative_velocity, normal) / inverse_mass_sum;
apply_impulse(a, -normal_impulse * normal, ra);
apply_impulse(b, normal_impulse * normal, rb);
// Recalculate after normal impulse
relative_velocity =
b.velocity + glm::cross(b.angular_velocity, rb) -
a.velocity - glm::cross(a.angular_velocity, ra);
glm::vec3 relative_momentum = relative_velocity / inverse_mass_sum;
// Apply friction
glm::vec3 friction_impulse;
if (glm::length2(relative_momentum) < glm::length2(normal_impulse * static_friction)) {
friction_impulse = -relative_momentum;
}
else {
friction_impulse = -normal_impulse * glm::normalize(relative_momentum) * dynamic_friction;
}
apply_impulse(a, -friction_impulse, ra);
apply_impulse(b, friction_impulse, rb);
}
当我运行这段代码时,它在 static_friction
和 dynamic_friction
的 1、2 或 3 等较低值上运行良好,即使它看起来有点滑动。但是,当我将它们都提高到,比如说,9999 时,它会立即 react 过度,以极快的速度抛开物体。它不应该这样做,因为 resolve_collision
的最后一个 if
语句应该用于“钳制”摩擦脉冲,使相对速度正好为零。但它似乎并没有这样做。
当将摩擦力设置为 10-20 左右时,球的线速度和角速度似乎来回摇晃。
我在这里做错了什么?我认为最有可能出错的代码部分是:
float inverse_mass_sum = a.inverse_mass + b.inverse_mass +
glm::length2(a.inverse_inertia * glm::cross(ra, normal)) +
glm::length2(b.inverse_inertia * glm::cross(rb, normal));
但我不知道这是怎么错的。我想补充一点,我可以只用一个移动的物体来做到这一点:
for (auto& platform : platforms) {
glm::vec3 contact_point;
if (intersects(platform, ball, contact_point)) {
// Only react if moving towards the platform
if (glm::dot(ball.position - contact_point, ball.velocity) > 0) {
continue;
}
// Collision with the platform applies a normal impulse
glm::vec3 normal = glm::normalize(ball.position - contact_point);
float normal_impulse = -ball.mass * glm::dot(normal, ball.velocity);
ball.velocity += normal_impulse * normal;
// Apply friction
glm::vec3 relative_momentum = ball.mass * ball.velocity +
inertia(ball) * glm::cross(ball.position - contact_point, ball.angular_velocity);
apply_impulse(
ball,
friction_impulse(relative_momentum, normal_impulse),
contact_point - ball.position);
}
}
friction_impulse
被定义为:
glm::vec3 friction_impulse(glm::vec3 relative_momentum, float normal_impulse)
{
if (glm::length2(relative_momentum) < glm::length2(normal_impulse * static_friction)) {
return -relative_momentum;
}
else {
return -normal_impulse * glm::normalize(relative_momentum) * dynamic_friction;
}
}
请随意将其移至 gamedev 或任何最合适的位置,我不确定该放在哪里。
最佳答案
您正在为您的正常分辨率正确计算您的反质量(我认为,掠过一瞥)。但是,您需要为摩擦分辨率计算一个不同的反质量和。
在计算摩擦项时,您将 normal
的所有实例交换为 tangent
vector 。
Vec3 raCrossT = Cross( ra, tangent );
Vec3 rbCrossT = Cross( rb, tangent );
real inverseMass = a.inverse_mass + b.inverse_mass;
inverseMass += Dot( raCrossT, a.inverse_inertia * raCrossT );
inverseMass += Dot( rbCrossT, b.inverse_inertia * rbCrossT );
然后您将专门为摩擦冲量计算一个新的冲量标量。
基本上,您是在完全隔离的脉冲之间混合计算。我看到你重新计算了相对速度,这很好!您还应该计算一个单独的反质量和,因为摩擦约束看到的反质量和会有所不同。
要求解切线 vector ,您可以使用相对速度和接触法线,并求出碰撞期间沿切线 vector 的速度:
Vec3 tangentVelocity = vRel - normal * Dot( vRel, normal );
Vec3 tangent = tangentVelocity.Normalized( );
幸运的是,Glenn 最近发布了他的一些 3D 摩擦和接触解析代码供您检查:http://gafferongames.com/virtualgo/collision-response-and-coulomb-friction/
关于c++ - 为什么这个模拟中的摩擦力会使物体以不稳定的方式运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20871485/