NVIDIAGameWorks / PhysX

NVIDIA PhysX SDK
Other
3.16k stars 802 forks source link

convex - trianglemesh collision inaccuracies #468

Open asdasdasdasdasdf opened 3 years ago

asdasdasdasdasdf commented 3 years ago

I have a dynamic rigid body (articulation link) interacting with static triangle mesh geometry (a tunnel if that's important). The region of the static geometry where they interact is more or less flat (normal to gravity axis). I'm rotating the dynamic body with a joint and when it collides with the ground, as I continue to push I expect it to lift. If this sounds like #452 it's because it's a new issue in the same setup.

What I observe is that low-penetration collisions which are clearly present in my diagnostic rendering of the two geometries are not detected by PhysX. Once the collision is eventually detected there is a lot of penetration and a violent depenetration occurs. Stepping through frame by frame I can see that on the frame where the collision is resolved the dynamic body is sitting exactly on the static surface but on subsequent ticks its momentum carries it further up.

How should I mitigate this? I'm using PhysX 4.1.2. To the best of my knowledge I'm using default cooking settings and collision detection settings on the scene. To be clear, I'm not super concerned about minor collisions not being detected right away. The effect I primarily want to avoid is the violent depenetrations which occur as the collisions get worse before they are detected.

Is convex-trimesh collision detection expected to be "correct" or does it have limitations of approximation?

I've found quite a lot of settings I could experiment with that sound peripherally related but I'm not sure which to focus on.

So far I have two ideas:

  1. reduce the complexity of the convex mesh. The surface on which I expect it to be supported has some thin triangles which I expected physx to be able to handle but perhaps cause it trouble. If I change cooking settings to planeTolerance = 0.03 I end up with simpler geometry which doesn't seem to exhibit the problem. The concern I have with this solution is that I still don't understand the underlying cause so I'm not sure to what extent I should rely on this fix.
  2. modify contacts to implement a maximum depenetration distance/force. I haven't tried this. I think theoretically it would solve my problem but might result in some weird floaty effects as excessive penetation is slowly corrected.

Basically I'm looking for advice on what capabilities I should expect from PhysX in this area so I can tell whether I'm doing something wrong or PhysX and, either way, what I should do about it.

Thanks

kstorey-nvidia commented 3 years ago

Can you share a PVD capture? What are the contact distance parameters that you are using? PhysX uses persistent manifolds so it will generate a set of contacts, then attempt to recycle/incrementally update those contacts. If you increase contact distance, it can increase the set of contacts in the manifold, which could avoid these cases where contacts are missed.

Also, related to (2), you don't need to implement contact modification to achieve this. PxRigidBody::setMaxDepenetrationVelocity should give you what you need - it allows you to define the maximum velocity that can be used to correct penetrations. It is defaulted to MAX_F32, but you can reduce this to something smaller such as 2-3m/s and you will no longer have such violent de-penetrations. However, you should ensure you do not set this too small because this can damage stable stacking.

asdasdasdasdasdf commented 3 years ago

Thanks for the quick response.

Unfortunately I'm running over remote desktop at the moment so I can't generate a PVD capture. I can hopefully get one later in the week.

I'm using default contact distance (I never call setContactOffset). I have PxTolerancesScale::length set to 1 so the contact distance would be (according to the docs) 0.02m. Interestingly I just measured and the collision is finally resolved at what appears to be a penetration of 0.02m. Not sure if that's a coincidence or not. My understanding was that the effective contact distance was the sum of values for the two shapes so would actually be 0.04m. I was skeptical about the contact distance helping since there are 10 or so ticks where the penetration is increasing roughly linearly so I don't think it's a question of failing to notice an impending contact. I'll try increasing though and see what impact it has. Is there some maximum number of contact points which make up the manifold which I should be increasing?

EDIT: I'll keep setMaxDepenetrationVelocity in mind as well

kstorey-nvidia commented 3 years ago

There is a maximum number, but not one that you can change without modifying the PCM code.

I'll take a look at your PVD capture when you have it and perhaps can advise better at that point.

asdasdasdasdasdf commented 3 years ago

Indeed it looks like increasing the contact offset from 0.02 to 0.04 has solved the problem but I'm quite confused as to why. With the default value of 0.02 the bodies interpenetrate by 0.02m (again might be coincidence) before fully depenetrating. I thought that the contact should be added to the manifold when they're separated by 0.02m rather than penetrated by 0.02m. If this were the case then I could see that 0.04 might give a more stable reaction as it would give a more complete picture of the impending contact but shouldn't some contact be enough?

Either way, thanks for your help so far.

asdasdasdasdasdf commented 3 years ago

I've Uploaded Missed Collision.pdx2 to the FTP. The body in question is part of the one articulation in the scene. Compare frame 1896 where no collision is detected despite there being obvious penetration to frame 1897 where the collision is detected and 1898 where it is resolved.

The contact distance 0.04m didn't end up working for me because

  1. I found a case where 0.04 was insufficient so I had to increase it to 0.1
  2. I rely on collision callbacks to know when bodies are touching and these kick in as soon as it's within 0.12 now.

setMaxDepenetrationVelocity solution seems to work though it makes the reaction a bit more spongy (not in a way that's super noticeable) but I'd still like to understand the problem and how to avoid the root cause.

thanks

kstorey-nvidia commented 3 years ago

I think this one is almost certainly related to a recycling tolerance. The motion of the bucket is very slow so it's not CCD-related. The recycling tolerances are based on the size of the convex and they are probably just coming in a bit too large.

I suspect that we need to add in a lower-bound limit based on contact offset to make sure that we aren't going to miss contacts when simulating really large shapes.

If you want a quick fix workaround, take a look in

bool Gu::pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS)

at this variable:

const FloatV minMargin = CalculatePCMConvexMargin(hullData, vScale, toleranceScale, GU_PCM_MESH_MANIFOLD_EPSILON);

and scale down that tolerance limit. It should improve this issue. we'll try to revisit this on my side and add in an additional heuristic based on contact offsets that should be more robust to this. I can't give you an ETA on when we'll be able to do that at the moment.

An alternative to adjusting the recycling thresholds is to increase the contact offset, which ensures we generate contacts while they are separated and makes sure the recycling doesn't cause us to miss contacts.

asdasdasdasdasdf commented 3 years ago

cool thanks. I'll give that a go when I get a chance.

When you say really large shapes are you talking about the dynamic body or the static terrain? I am surprised if you're referring to the bucket since it's only maybe 4x the length scale.

kstorey-nvidia commented 3 years ago

I am referring to the bucket. I suspect the issue is the very slow moving, large shape exacerbating the issues. I've not seen this kind of artefact with large objects before, but it does seem likely that it's related to us having an empty manifold and then only triggering contact gen once the relative transforms of the shapes have moved beyond a certain amount.

asdasdasdasdasdf commented 3 years ago

Ah that makes sense. One thing though, according to the docs I have to set eENABLE_PCM in order to be using PCMs and I'm not setting that flag. Does that affect your theory or proposed solution?

kstorey-nvidia commented 3 years ago

With PhysX 4, PCM is on by default. The legacy collision approach has also been augmented to add in some contact caching, although it uses a different approach to generate, track and update contacts.

asdasdasdasdasdf commented 3 years ago

Oh that is not obvious in the documentation. Thanks for clarifying.

Is there a way to disable the caching entirely or per body? I'd be interested to see the impact on performance in my case where there are relatively few bodies.

kstorey-nvidia commented 3 years ago

Not without changing the code on your end. It's certainly something that could be added, but the hope is that this should be completely transparent to the user. It's super easy to make it do collision all the time if you want to edit that function.

change this

if(multiManifold.invalidate(curTransform, minMargin))

to

if(multiManifold.invalidate(curTransform, minMargin) || 1)

and it will go into contact gen regardless of the results of the manifold invalidation logic.

asdasdasdasdasdf commented 3 years ago

alright thanks a lot for all your help!