如何解决物理对象在连续接触时会“漂移”
我一直在尝试制作一个简单的3D物理引擎作为练习。我的问题是,如果对象的位置未完全对齐,则它们在与另一个对象接触时会“漂移”。
在我的测试用例中,顶部的框启用了物理功能,并受重力影响。底部框是静态的(速度固定为0)。如果顶盒完全位于底盒中心上方(因此它们共享相同的X和Z坐标),则顶盒将落在底部并保持完全静止。但是,如果顶盒由于事件而在X轴或Z轴上稍微偏移了一点,那么它着陆后将开始在该方向上获得动量,直到最终掉下为止,如here所示。
我知道是什么原因造成的:当着陆点居中时,EPA提供的接触点(我根据this实现)位于顶部框中心的正下方。这将导致雅可比决定约束施加到顶盒的扭矩( r1 x normal )的部分为0。但是,偏移时,接触点并不位于顶盒的正下方。盒子的中心,导致它稍微旋转。这进而导致下一时间步长的接触法线稍微旋转。顶盒沿该接触法线推出,导致其滑动。我已经确认了这一点,因为禁用旋转或硬编码以将法线与0,-1,0接触都可以解决此问题。
我认为实现联系人缓存可以解决此问题,但是如上面的视频所示,每个紫色点代表一个活动的联系人,情况并非如此。我在多个时间步上缓存联系人,并且每当联系人向对象施加力时,它都会更新与该对象(包括自身)有关的所有联系人的穿透深度:
private void ApplyForces(RigidBody Body,Time deltaTime,vec3 deltaVel,vec3 delTarot)
{
Body.ForcesConstraints += deltaVel;
Body.TorqueConstraints += delTarot;
foreach (Constraint c in M.InvolvedConstraints)
c.UpdateConstraint(Body,deltaTime,deltaVel,delTarot);
}
public override void UpdateConstraint(RigidBody Body,vec3 delTarot)
{
//I understand that this way of computing the actual positional delta the delTarot represents is incredibly bad,but I coulnd't get anything else to work (I'm a linear algebra newbie).
var rot = (quat.FromAxisAngle((deltaTime * delTarot).Length,delTarot.normalizedSafe) * contact) - contact;
var deltaPos = (deltaVel * deltaTime) + rot;
//The contact normal points from Body1 to Body2
_pendepth += (Body == Body1 ? 1 : -1) * vec3.Dot(deltaPos,_normal);
}
解决方法
这个问题几乎可以肯定是由于非常小的数字随着时间的流逝而有所不同。滑动很小。我克隆了您的存储库,但是我没有运行它的库,因此,我唯一能给您的建议就是阅读代码。
但是当我看代码时,这样的事情让我真的很紧张:
int index = -1;
float maxDot = -9999;
float td = -9999;
float ti = -1;
我知道您要做什么,但是过去这种事情导致很多很难为我找到错误的事情。
关于您的Jacoabian问题,如果我没记错的话,当雅可比矩阵为0时,这是一个奇点,而您的控制力根本无法移动它。
您似乎可以肯定确实确实是由于表面之间的很小角度引起的。我最好的想法是,如果两个表面几乎平行(因此,三角形角度低于某个阈值,则可以禁用该物理过程的某些部分。这可能涉及将对象“捕捉”到稳定状态,以描述您在哪里他们正好对齐。
如此:
public static float MinAngleThreshold = 0.001f;
public override void UpdateConstraint(RigidBody Body,Time deltaTime,vec3 deltaVel,vec3 deltaRot)
{
//I understand that this way of computing the actual positional delta the deltaRot represents is incredibly bad,but I coulnd't get anything else to work (I'm a linear algebra newbie).
var rot = (quat.FromAxisAngle((deltaTime * deltaRot).Length,deltaRot.NormalizedSafe) * contact) - contact;
if(rot < MinAngleThreshold)
{
//do something here,potentially just return without updating.
}
var deltaPos = (deltaVel * deltaTime) + rot;
//The contact normal points from Body1 to Body2
_pendepth += (Body == Body1 ? 1 : -1) * vec3.Dot(deltaPos,_normal);
}
虽然您的代码肯定存在一些问题,但是整个系统非常聪明,您应该为自己编写的内容感到骄傲。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。