以下用于解决两个对象之间的冲突的代码假定重置为零。主体的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时,它会立即反应过度,以极高的速度甩开物体。不应这样做,因为if的最后一个resolve_collision语句应用于“夹紧”摩擦力,使相对速度恰好为零。但这似乎没有做到。

将摩擦力设置为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向量。

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 );


然后,您将专门为摩擦脉冲计算一个新的脉冲标量。

基本上,您是在完全隔离的脉冲之间混合计算。我看到您重新计算了相对速度,这很好!您还应该计算一个单独的反质量总和,因为摩擦约束看到的反质量总和将有所不同。

要求解切线向量,可以使用相对速度和接触法线,并在碰撞过程中沿切线向量找到速度:

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/

10-11 17:20