🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Resolving contact and frictional forces using constraints

Started by
8 comments, last by Dirk Gregorius 5 years, 7 months ago

I've been trying to get my head around constrained based collision response algorithms by reading Erin Catto's paper as well as Allen Chou's article on the subject. I'm a bit confused as to how contact and frictional forces are dealt with. Allen Chou mentions that, for bodies with multiple constraints such as prismatic joints, the lambda value becomes a vector due to the constraint matrix having multiple rows. I couldn't find any mention about whether or not friction and contact forces are resolved using this method, or instead independently (i.e. calculate different lambdas using separate contact and friction constraints and calculating separate delta Vs).

Is there a significant performance difference between the two methods? Thanks in advance.

Advertisement

I've tried implementing the PGS method in Erin Catto's paper to resolve collisions. Currently I have not implemented warmstarting. Stacking works well when friction is disabled. However, when I add friction, dropping a box from a height causes it to wobble significantly and fall over when hitting the ground.

My algorithm currently adds all constraints for all contact points into a single jacobian (labelled j), and follows this order:

j[0] = contactNormalConstraint [ contactPoint 0]

j[1] = contactFrictionConstraint [ contactPoint 0]

j[2] = contactNormalConstraint [ contactPoint 1]

j[3] = contactFrictionConstraint [ contactPoint 1]

 

I then feed j into the PGS solver (using the naive initial guess of lambda = 0 for all i). The PairWiseVel and PairWiseMass structs just contain linear and rotational velocities, and mass (inertia) respectively.

 


void Constraints::solveConstraints(CStructs::PairWiseVel &returnVel,
                                std::vector<CStructs::Constraint> &j,
                                CStructs::PairWiseMass &pwm,
                                std::vector<float> &lambda)
{
    std::vector<float> d;

    for(int i=0; i<j.size(); ++i)
    {
        d.push_back(getDenom(j[i], pwm));
    }
    for(int c=0; c<5; ++c)
    {
        for(int i=0; i<d.size(); ++i)
        {
            float dlambda =  - (multiply(j[i], returnVel) + j[i].bias)/d[i];

            float l0 = lambda[i];
            lambda[i] = std::max(j[i].lambdaMin, std::min(l0 + dlambda,
                                                          j[i].lambdaMax));
            dlambda = lambda[i] - l0;

            returnVel.v1 = returnVel.v1 + dlambda * j[i].c1 / pwm.m1;
            returnVel.v2 = returnVel.v2 + dlambda * j[i].c2 / pwm.m2;
            returnVel.w1 = returnVel.w1 + dlambda * j[i].cw1 / pwm.i1;
            returnVel.w2 = returnVel.w2 + dlambda * j[i].cw2 / pwm.i2;
        }
    }

}

Is it likely that the lack of warmstarting causes this instability? At 50 iterations, a dropped box doesn't fall over when hitting the floor, but a stack of two or more falls immediately. I am just wondering if there's something I've done wrong before implement warmstarting.

Hi,

The way I've implemented is by using two friction constraints separate of the contact constraint. (Each friction constraint is tangent to the contact normal, and perpendicular to the other friction constraint). The friction constraint must be clamped between the friction coefficient multiplied by the contact constraint impulse, and the negative of this value.

The important thing to note here is that the friction constraint's clamping values depend on the contact constraint's impulse. That means each time you update the contact constraint, you must update the clamping values of the friction constraints before you iterate on them.

It is possible you have already done this, but I don't see it in your code and you haven't explicitly mentioned it. Friction should work fine without warm starting, and you should definitely not need 50 iterations.

Hi Max,

Thanks for your reply. I decided to redo my collision engine by essentially following/porting the Box2d Lite code (after I understood it properly). General behaviour is a lot better than before and I haven't even implemented warmstarting yet. Box stacking is still quite weak. There's almost no jitter but I find that even a stack of 10 boxes will fall over within a few seconds @ 10 iterations. I know warmstarting would improve the results but is this standard behaviour? I just want to make sure what I have so far is working properly.

That sounds about correct. Even with warmstarting the stack will sway a bit before it becomes stable. Also make sure you use a linear slop. This is important for contacts to persist between frames.

Cheers Dirk. I have one more question regarding the restitution bias: Should the bias be updated every iteration or is it better to set the bias during the prestep only? I'm assuming it needs to be updated every iteration since the velocity values of the collision pair also change every iteration, and the restitution depends on these velocity values.

You only set the restitution bias once per prestep. There is a small gotcha though. Make sure you compute the relative approaching velocity for the restitution bias *before* you apply the warm-starting. Otherwise you will use wrong velocities.

I would skip restitution initially though. There are very few objects that really need restitution. Designers sometimes thinks that you need restitution for stuff to tumble over, but this not the case. Restitution makes objects look lighter than they are most of the time which I personally try to avoid. There are a few exceptions though. E.g. ammunition shells.

 

HTH,

-Dirk

Ah okay. Does that mean I should also calculate the tangent vector in the pre-step? I'm using this formula

\(\mathbf{t} = \textit{norm}(\mathbf{v}_{ab} - ( \mathbf{v}_{ab} \cdot \hat{\mathbf{n}} ) \hat{\mathbf{n}})\)

Which also depends on the relative velocity \(\mathbf{v}_{ab}\).

EDIT: Well I just tried my simulation with a tangent vector calculated from the cross product with the normal vector. I was surprised to see friction work as I was expecting a difference in behaviour when the tangential vector happened to be in the same direction as the relative velocity. Is this due to the clamping mechanism preventing friction from adding kinetic energy?

Yes, and you also need a fallback if the relative velocity in the tangent plane becomes zero. Otherwise t will be the zero vector.

This topic is closed to new replies.

Advertisement