Quote:Original post by DonDickieD
I remember now why I use two friction impulses. Basically I can't see how you can accumulate the friction impulse when the basis is changing during the iteration?
You see what I mean? In my opinion you need to project onto a staionary basis, while otherwise you are accumulating vectors (length) with changing direction which is not correct in my opinion.
Indeed. During the iterations, the velocity changes, and if you just one one friction direction, there will be no friction correction orthogonal to the normal and friction direction. This will result in very minor drift. This is noticeable in the Bullet CcdPhysicsDemo with a stack of cylinders (you can disable/comment out the second friction direction to see this).
Quote:
Anyway it is good for the quality of the friction to align the first tangential axes with the relative velocity in contact plane (if it exists).
Indeed, so in Bullet we use the projected normal onto the separating plane as first friction direction, and the orthogonal direction (normal.cross(firstFriction)) as second, unless the projected normal length is zero. In that case you still better use 2 orthogonal friction axis (during the iterations, the velocity changes and makes the projected normal non-zero).
Note that the straighforward accumulated impulse clipping results in 'box' shaped friction model, which in certain cases can cause objects to slide in unnatural directions: this happens when the friction gets clipped onto one of the box axis but not the other. This effect is reduced when using the projected normal as first friction direction.
Also note that we are making a lot of approximations. We can use the current normal impulse for the magnitude for the friction impulse. However, we have the 'chicken-and-egg' problem here, because friction and normal impulse have a two-way effect, ideally we solve a bigger system. However, the relaxation/iterative process usually converges fine and hides all those approximations.
To make a long story shorter: indeed, impulse based friction correction the same as normal collision correction, just using a different axis, and using an impulse magnitude dependent on its related normal collision impulse. That is why the friction has to happen at the end of all iterations. An alternative is to use the approximation of previous frames' normal impulse magnitude. Then the challenge becomes how to come up with an approximation the very first frame (otherwise a single-frame collision would not result in any friction).
To finish with the snippet the Bullet currently uses:
btVector3 frictionDir1 = vel - cp.m_normalWorldOnB * rel_vel; btScalar lat_rel_vel = frictionDir1.length2(); if (lat_rel_vel > SIMD_EPSILON)//0.0f) { frictionDir1 /= btSqrt(lat_rel_vel); addFrictionConstraint(frictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,rb0,rb1, relaxation); btVector3 frictionDir2 = frictionDir1.cross(cp.m_normalWorldOnB); frictionDir2.normalize(); addFrictionConstraint(frictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,rb0,rb1, relaxation); } else { //re-calculate friction direction every frame, todo: check if this is really needed, because we use persistent contacts that keep the same normal btVector3 frictionDir1,frictionDir2; btPlaneSpace1(cp.m_normalWorldOnB,frictionDir1,frictionDir2); addFrictionConstraint(frictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,rb0,rb1, relaxation); addFrictionConstraint(frictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,rb0,rb1, relaxation); }
Hope this helps,
Erwin
http://bulletphysics.com