确定使用外力时球体和平面之间的静止接触

Determining Resting contact between sphere and plane when using external forces

本文关键字:平面 之间 接触 静止      更新时间:2023-10-16

这个问题有一个大问题,一个小问题。我相信我的研究中任何一个问题都是对的,但不是两个问题。

对于我的物理循环,我要做的第一件事是对刚体物体的TotalForce施加引力。然后,我使用TotalForceVelocity检查碰撞。我的TotalForce在每次物理循环后都会重置为(0, 0, 0),尽管我会保持velocity

我熟悉在仅使用速度时在移动球体和静态平面之间进行碰撞检查。但是,如果我除了velocity之外还有其他力,例如重力怎么办?我把其他力放到TotalForces(现在我只有重力(。为了弥补这一点,当我确定球体当前没有与平面重叠时,我这样做

    Vector3 forces = (sphereTotalForces + sphereVelocity);
    Vector3 forcesDT = forces * fElapsedTime;
    float denom = Vec3Dot(&plane->GetNormal(), &forces);

然而,对于我认为应该是休息接触的方式来说,这可能是有问题的。我以为休息接触是由下式计算的

denom * dist == 0.0f

dist在哪里

float dist = Vec3Dot(&plane->GetNormal(), &spherePosition) - plane->d;

(作为参考,明显的denom * dist > 0.0f意味着球体正在远离平面(

然而,这永远不可能是真的。即使似乎有"休息接触"。这是由于我上面的forces计算总是至少有 -9.8(我的重力(的 .y。当向法线为 (0, 1, 0( 的平面移动时,将产生 -9.8 的 denom y 值。

我的问题是

1( 我是否正确计算了我提到的前两个代码片段的静止接触?

2(我的"其他力"(如重力(应该如何使用?我对TotalForces的使用不正确吗?

作为参考,我的时间步长是

  mAcceleration = mTotalForces / mMass;
  mVelocity += mAcceleration * fElapsedTime;
  Vector3 translation = (mVelocity * fElapsedTime);

编辑

由于一些建议的更改似乎会更改我的碰撞代码,以下是我检测碰撞状态的方法

if(fabs(dist) <= sphereRadius)
{ // There already is a collision }
else
{
    Vector3 forces = (sphereTotalForces + sphereVelocity);
    float denom = Vec3Dot(&plane->GetNormal(), &forces);
    // Resting contact
    if(dist == 0) { }
    // Sphere is moving away from plane
    else if(denom * dist > 0.0f) { }
    // There will eventually be a collision
    else
    {
        float fIntersectionTime = (sphereRadius - dist) / denom;
        float r;
        if(dist > 0.0f)
            r = sphereRadius;
        else
            r = -sphereRadius;
        Vector3 collisionPosition = spherePosition + fIntersectionTime * sphereVelocity - r * planeNormal;
    }
}
  1. 您应该使用 if(fabs(dist) < 0.0001f) { /* collided */ } 这是为了计算浮点精度。您肯定不会在大多数角度或接触中获得精确的 0.0f。

  2. 如果为负值,则dist的值实际上是将主体移回平面表面所需的实际量,以防它穿过平面表面。 sphere.position = sphere.position - plane.Normal * fabs(dist);

  3. 一旦你把它移回表面,你可以选择让它在与平面正常方向相反的方向上反弹;或者只是留在飞机上。

    parallel_vec = Vec3.dot(plane.normal, -sphere.velocity);

    perpendicular_vec = sphere.velocity - parallel_vec;

    bounce_velocity = parallel - perpendicular_vec;

  4. 你不能盲目地做totalforce = external_force + velocity除非一切都有单位质量。

编辑

  1. 要在 3D 空间中完全定义平面,平面结构应存储平面法线向量和平面上的点。 http://en.wikipedia.org/wiki/Plane_(几何( .

Vector3 planeToSphere = sphere.point - plane.point;

float dist = Vector3.dot(plane.normal, planeToSphere) - plane.radius;

if(dist < 0) { // collided. }

我建议你先学习更多的数学,如果这是你不知道的部分。

注意:对不起,格式搞砸了...我无法将其标记为代码块。

编辑2:根据我对你的代码的理解,要么你命名变量不好,要么像我前面提到的,你需要修改你的数学和物理理论。此行不执行任何有用的操作。

float denom = Vec3Dot(&plane->GetNormal(), &forces);

A 在任何时间实例中,球体上的力可以是与行进方向无关的任何方向。 因此,DENOM基本上计算的是平面方向上的力,但不会告诉您球是否会撞击平面。 例如,重力是向下的,但球可以具有向上的速度并击中上方的平面。有了这个,你需要Vec3Dot(plane.normal, velocity)

另外,Mark Phariss 和 Gerhard Powell 已经为您提供了线性运动学的物理方程,您可以使用它们直接计算未来的位置、速度和撞击时间。

例如 s = 0.5 * (u + v) * t;给出未来时间t之后的位移,将该位移与与平面的距离进行比较,就可以得到球体是否会撞击平面。所以再一次,我建议你先阅读 http://en.wikipedia.org/wiki/Linear_motion 和简单的东西,然后再阅读 http://en.wikipedia.org/wiki/Kinematics。

还有一种方法,如果您期望或假设没有其他力作用在球体上,那么您进行射线/平面碰撞测试以找到它撞击飞机的时间t,在这种情况下,读取 http://en.wikipedia.org/wiki/Line-plane_intersection 。

总会有-9.8y的重力作用在球体上。在悬浮球体的情况下,这将导致向下加速(净力不为零(。在球体停留在平面上的情况下,这将导致平面在球体上施加法向力。如果平面与静止的球体完全水平,则此法向力正好为+9.8y,这将完美地抵消重力。对于在非水平平面上静止的球体,法向力为 9.8y * cos(angle)(角度介于 -90 和 +90 度之间(。

当移动的球体撞击平面时,事情变得更加复杂,因为法向力将取决于速度和平面/球体材料属性。根据您的应用要求,您可以忽略这一点,也可以尝试使用法向力进行一些操作,看看它是如何工作的。

对于您的具体问题:

  1. 我相信接触更具体地说只是在dist == 0.0f的时候,即球体和平面正在接触。我假设你的碰撞考虑到球体可能会在任何物理时间步长中移过平面。
  2. 现在,当它们接触时,你似乎没有任何法向力从平面施加在球体上。我会通过检查接触(dist == 0.0f(来做到这一点,如果这是真的,则将法向力添加到球体中。在将球体落到近水平面(角度在 -90 到 +90 度之间(的简单情况下,它只是sphereTotalForces += Vector3D(0, 9.8 * cos(angle), 0) .

编辑:

从这里开始,dist计算从球体边缘到平面的距离的方程可能不正确,具体取决于问题和代码的细节(未给出(。假设您的飞机穿过原点,正确的方程为:

dist = Vec3Dot(&spherePosition, &plane->GetNormal()) - sphereRadius;

如果plane->d == sphereRadius,这与你的方程相同。请注意,如果平面不在原点,则使用:

D3DXVECTOR3 vecTemp(spherePosition - pointOnPlane);
dist = Vec3Dot(&vecTemp, &plane->GetNormal()) - sphereRadius;

这个问题的确切解决方案涉及一些非常严肃的数学。如果你想要一个近似的解决方案,我强烈建议你分阶段开发它。

1(确保您的模拟市民在没有重力的情况下工作。球必须穿过空间,并与倾斜的无摩擦表面发生非弹性(或部分弹性(碰撞。

2(引入重力。这将改变弹道轨迹从直线到抛物线,并引入滑动,但它不会对碰撞产生太大影响。

3(引入静态和动力摩擦(独立(。这些将改变滑动的动态。暂时不要担心碰撞中的摩擦。

4(给出球角速度和转动惯量。这是一大步。确保您可以对其施加扭矩并获得逼真的角加速度。请注意,旋转质量的实际行为可能是违反直觉的。

5(尝试在重力下沿水平表面滑动球。如果你做对了一切,它的角速度会逐渐增加,它的线速度会逐渐降低,直到它变成滚动。尝试给球一些初始旋转("平局","跟随"或"英语"(。

6(尝试相同的方法,但在倾斜的表面上。这是一个相对较小的步骤。

如果你走到这一步,你将拥有一个非常逼真的模拟游戏。不要试图跳过任何步骤,你只会让自己头疼。

物理问题的答案:

f = mg + other_f; // m = mass, g = gravity (9.8)
a = f / m; // a = acceleration
v = u + at; // v = new speed, u = old speed, t = delta time
s = 0.5 * (u + v) *t;

发生碰撞时,如果希望速度反弹,请将两个速度都更改为 0(或 v 和 u = -(u * 0.7(。

因为速度 = 0,所以球静止不动。

如果是 2D 或 3D,则只需将曲面法线方向上的速度更改为 0,并保持平行速度不变。这将导致球在表面上滚动。

如果球切开表面,则必须将球移动到表面。您可以将碰撞距离设置为少量(例如 0.001(以确保其保持静止。

http://www.physicsforidiots.com/dynamics.html#vuat

编辑:

NeHe是游戏引擎设计的惊人来源:这是一个关于碰撞检测的页面,有很好的描述:http://nehe.gamedev.net/tutorial/collision_detection/17005/

编辑2:(来自NeHe(

double DotProduct=direction.dot(plane._Normal); // Dot Product Between Plane Normal And Ray Direction
Dsc=(plane._Normal.dot(plane._Position-position))/DotProduct; // Find Distance To Collision Point
Tc= Dsc*T / Dst
Collision point= Start + Velocity*Tc

我建议之后看看erin cato的文章(Box2D的作者(和Glenn fiedler的文章。重力是一种强大的加速度,并产生强大的力。由于浮动不精确、可变时间步长和欧拉积分,很容易很快出现错误的模拟。球体在平面上重新定位,以防它开始通过平面,如果它开始埋入平面,我注意到自己最好只在球体的速度与平面法线相反时才这样做(这可以与 3D 渲染中的面部剔除进行比较:不要考虑背面平面(。

此外,大多数

物理引擎会停止对空闲物体的模拟,大多数游戏在移动时从不考虑重力,只有在坠落时才会考虑重力。他们使用"导航网格"和自定义系统,只要他们确定模拟对象粘在"地面"上即可。

我不知道那里有完美的物理模拟器,总会有集成爆炸,错过碰撞(寻找"扫荡碰撞"(......这需要大量的实证微调。

此外,我建议您寻找"脉冲",这是一种避免在遇到碰撞时手动调整速度的方法。

另请查看"每个计算机科学家都应该知道的浮点">

祝你好运,你进入了一个雷区,随机无法理解,数字计算机科学:)咬手指的区域

为了获得更高的保真度(不会解决您的主要问题(,我会将您的时间步改为

mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;

提到球体是一个刚体;你是否也把平面建模为刚体?如果是这样,在接触和完全弹性碰撞的那一刻,你将有一个无限的点力,而没有一些明显的动量消散。

力和速度

不能相加(不兼容的单位(;如果你只是想对运动学进行建模,你可以忽略质量,只使用加速度和速度。

假设球体只是简单地放到一个水平面上,有一个完全非弹性的碰撞(没有反弹(,你可以做[注意,我真的不知道C语法,所以这将是Pythonic]

mAcceleration = if isContacting then (0, 0, 0) else (0, -9.8, 0)

如果你在碰撞中增加一些弹性(比如半动量守恒(,它更像是

mAcceleration = (0, -9.8, 0) + if isContacting then (0, 4.9, 0)