dimforge / rapier

2D and 3D physics engines focused on performance.
https://rapier.rs
Apache License 2.0
3.97k stars 248 forks source link

Incorrect friction behavior #137

Closed miolad closed 3 years ago

miolad commented 3 years ago

https://user-images.githubusercontent.com/66064386/109793418-e817dc00-7c14-11eb-98be-d2299cf1fa21.mp4

The video shows a top-down, fixed, orthographic view of a dynamic cuboid sliding on a static, bigger cuboid. Every force applied to the dynamic body is always parallel to its longer side and the body is rotated in place via a torque through its center of mass. Every collider has a friction coefficient of 1.5.

In the video, you can first see the body sliding along the horizontal axis of the screen (z axis in world space); everything is as expected. The cuboid is then rotated slightly and the same force is applied again (rotated according to the body's isometry). This time, the body, instead of going "straight ahead", still follows the -/+z axis. This is unexpected and shows that the friction must have a wrong (too big) component along the x axis (vertical in screen space). Finally, the body is rotated once more, so that the movement will be oblique with respect to the screen. In this case you can see that the friction still has a tendency to make the body slide in the horizontal drection.

An analogous behavior is also shown along the screen's vertical axis when the body's longest side forms an acute angle with said vertical direction.

Here is the code I'm using for controlling the dynamic body:

let rotation = rigid_body.position().rotation;

// Get the forward, right and up directions in world space
let forward_dir_world = rotation.transform_vector(&vehicle_controller.forward_dir);
let right_dir_world = rotation.transform_vector(&vehicle_controller.right_dir);
let up_dir_world = right_dir_world.cross(&forward_dir_world);

// Apply the forces
let force_multiplier = 150.0f32;
let torque_multiplier = 150.0f32;

if input_state.is_pressed(Button::Forward) {
    rigid_body.apply_force(forward_dir_world * force_multiplier, true);
}

if input_state.is_pressed(Button::Backward) {
    rigid_body.apply_force(-forward_dir_world * force_multiplier, true);
}

if input_state.is_pressed(Button::Right) {
    rigid_body.apply_torque(-up_dir_world * torque_multiplier, true);
}

if input_state.is_pressed(Button::Left) {
    rigid_body.apply_torque(up_dir_world * torque_multiplier, true);
}

It should be noted, though, that everything works as expected when disabling friction (by setting friction or gravity to zero) and instead faking it with linear and angular damping.

The version or rapier3d used is 0.6.1.

sebcrozet commented 3 years ago

Hi! Thank you for the example.

This is an artifact due to the way friction is modeled. We use Coulomb friction, except that the friction cone is approximated by a pyramid. This is a common approximation in game physics engines because it avoids one square root on a tight loop.

This approximation implies that friction is allowed to be slightly stronger in diagonal directions. In order to fix this, we need to switch to a non-approximated cone. This change will have a performance impact (not sure yet if this will be noticeable or not), but I believe this is necessary to ensure a better simulation quality.

extrawurst commented 3 years ago

How does the typical industry libraries handle this? Box2d and co? Do they behave the same?

sebcrozet commented 3 years ago

@extrawurst This problem does not exist in 2D because there is only one possible friction direction for a given contact in 2D. In 3D, Bullet physics used the approximation for a while, but switched to an actual cone afterwards (but they have an option to switch back to the old behavior). I believe PhysX uses the friction pyramid approximation but they make the problem less noticeable by using the relative velocity as the first friction direction.

sebcrozet commented 3 years ago

@miolad Could you please check that https://github.com/dimforge/rapier/issues/137 fixes your problem?

miolad commented 3 years ago

@miolad Could you please check that #137 fixes your problem?

Sorry for the delay, but yep, this totally fixes it, thank you!

Have you measured how much slower it is?

sebcrozet commented 3 years ago

@miolad Thank you for checking. It doesn't make any meaningful differences performance-wise (see https://www.rapier.rs/benchmarks/ by selecting implicit_friction_cone on the second drop-down). We are memory-bound as far as the solver is concerned, so I guess that adding a sqrt (and a bunch of other stuffs) isn't enough to make any difference.