c++ - 为什么这个模拟中的摩擦力会使物体以不稳定的方式运行?

标签 c++ physics game-physics

以下用于解决两个对象之间的碰撞的代码假定恢复为零。物体的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_frictiondynamic_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/

相关文章:

c++ - 哪个基类正在调用派生的重写方法?

c++ - C++ 中 'x % y != 0' 的机制

c++ - 零作为神经网络输入

ios - SpriteKit applyForce 不工作

c# - 二维游戏物理资源

c# - 碰撞时阻止移动

java - 与圆的弹性碰撞

c++ - Cocos2d-x : some sprites become invisible when a PhysicsWorld is set

c++ - 回调参数类型在继承类中不匹配

android - 如何实现锁定在 Y 轴上的对象跟随鼠标位置的平滑移动?