Advertisement

Help with 3D physics - having trouble getting accurate resolution with friction

Started by March 05, 2023 11:09 AM
3 comments, last by quou 1 year, 7 months ago

Hello,

I have been trying to make a 3D physics engine for about 2 weeks now and I've got something fairly primitive working. It uses Minkowski Portal Refinement to generate two contact points on the surfaces of each object, then I use a simple caching system with a hash table to generate multiple contact points over a few iterations, invalidating them when the distance between the two surfaces is too great. However, the collision resolution looks off a lot of the time, especially with rotation and friction.

I have a test scene with a static floor object and a dynamic cube object that's moving forwards at a velocity of 10 units per second, and falling due to gravity. I'm running it with the simulation speed set quite low so that I can see what's happening.

This video shows what it looks like with rotation, but no friction: https://cloud.disroot.org/s/DJrPSQka4we6pSp

As can be observed, the object has a little wobble (from the way my contact points move around, I think), but has mostly acceptable stability, at least for my purposes.
The red crosses are the contact points, and the lines drawn from their centres are the impulse vectors for the resolution.

However, when I add friction, this happens: https://cloud.disroot.org/s/RXR4bJcrr9NXi46

The object starts out looking alright when it first hits the floor, it starts to tip over because of its forward movement, which looks correct enough to me. However, when it fully tips over causing the other edge to touch the ground, it spins incorrectly and generally looks really bad. Again, the red crosses are the contact points, but the lines drawn from them are the friction impulse vectors.

I think what's happening is sometimes it generates huge friction impulses in the wrong direction which throw it out of whack and cause it to go spinning off into Narnia. I'm not entirely sure why, though, which is where I'd like a bit of help, maybe some pointers in the right direction at least.

This is how I'm generating and applying my friction impulse:

rv =
	b.velocity + b.angular_velocity.cross(rb) -
	a.velocity - a.angular_velocity.cross(ra);
	
t = -(rv - (n * rv.dot(n))).normalise();

jt = (-rv.dot(t)) / ms / divisor;

if (kindof(jt, 0.0f)) {
	return;
}

if (fabsf(jt) < j * sf) {
	impulse = t * jt;
} else {
	impulse = t * -j * df;
}

a.velocity -= a.inv_mass * impulse;
b.velocity += b.inv_mass * impulse;

a.angular_velocity -= a.inv_moment * ra.cross(impulse);
b.angular_velocity += b.inv_moment * rb.cross(impulse);

To explain some of my (admittedly bad) variable names: ms is the sum of both objects' inverse masses, divisor is the contact count as a real number, n is the normal, and ra & rb are the contact points relative to objects a and b respectively, kindof is a function that checks if a float is “close enough” to a value by a very small margin (1.192092896e-07). sf and df are the averages of the static and dynamic friction coefficients of each object.

My full collision resolution code is here: https://bin.disroot.org/?ee0f95956a2468b1#8S4Kuim614rySGwaZQ17UxonKYxAB3FNh5d5WDqFCnZ1

To explain it a bit, Contact_List is an array container of contacts. Each contact is stored as a pair of points, on the surface of and in the local space of each object. inv_moment is a 3x3 matrix containing the inverse moment of inertia for each object. In this simple case of cubes, I generate it with this formula and then take the inverse (1/x) of each element:

Taken from the physics PDFs that are linked down a bit more

My implementation is based on Randy Gaul's 2D physics tutorial - maybe there are more things that I need to do in 3D? I am having a little trouble finding resources for 3D physics. These guides were very helpful for me in understanding the maths, but they don't cover friction unfortunately: https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/physicstutorials/

Does anything look obviously wrong with what I'm doing? Is there something that I'm not taking into account or am I using an incorrect technique?

  1. You have only 1 tangent direction, but there are actually two. You need to generate a tangent frame (3x3 matrix) with two tangent vectors that are perpendicular to each other and the normal.
  2. To get good behavior, you need some kind of iterative solver (e.g. sequential impulse). Otherwise, resting contact with multiple contact points will not be stable. Key words: “accumulated impulse”. This is important for making it converge to correct values.
  3. I would recommend looking at older versions of bullet physics library (before 2.7-ish). The newer versions have a rewritten solver which is much harder to understand.
  4. It's better to combine friction values with geometric mean (sqrt(a*b)) rather than simple average.
  5. Choose between static and kinetic friction by comparing current relative velocity at contact point to a threshold speed (e.g. 0.01 m/s).
  6. Your clamping is weird. Why do you use static friction in if() {} but dynamic friction in else {}? You just want to clamp jt to be no bigger than j*friction or less than -j*friction. You need to do the clamping separately for each tangent direction to produce a “friction pyramid”. It's also possible to clamp to a conic ellipse for better accuracy, this means calculating sqrt( jtX*jtX + jtY*jtY ) and scaling down both jtX and jtY if the magnitude is outside the valid ellipse.
Advertisement

Thanks

After doing some more reading about stability, I think what I'm going to do now is write a constraint solver and re-write my collision resolution code as a constraint.

As to what you said about there being multiple tangent vectors, this actually makes a lot of sense to me now, as the same thing is required with normal mapping in graphics where there's a bitangent and tangent vector - I didn't think about this before.

Thank-you for the suggestion about reading older Bullet code too - I'll certainly take a look.

I'll implement a constraint solver and my collision resolution as a constraint this week and I'll reply back here when I have something to show. Thanks again for your help ?

The suggestion to look at old Bullet source was absolutely the best advice anyone could have ever given me. Between that and React Physics 3D, I've learnt so much over the past few days. My rigidbody engine has become fairly stable now. It isn't the most accurate it could be, but the results it generates are at least somewhat plausible.

This topic is closed to new replies.

Advertisement