太阳系随机模拟碰撞检测问题

时间:2019-01-11 01:12:37

标签: c opengl collision-detection orbital-mechanics

因此,我使用运动的基本方程和牛顿力学以及将OpenGL与C一起开发了一个相当稳定的带有随机行星的太阳系模拟。

当行星彼此碰撞或与太阳碰撞时,我还进行了一些碰撞检测。

我的问题是,使用我当前的代码可以检测到碰撞,并相应地移除了行星,但是,当查看仿真时,行星可能会在重叠之前碰撞/碰撞它们半径的一半(只是基于我所看到的猜测)。碰撞被“检测到”。

我可以肯定我的碰撞检测方法已经足够,但是如果我错了,请纠正我。

我能想到的唯一另一个问题是,世界坐标,眼睛坐标等的“单位”之间存在一些差异。

非常感谢您提供的所有帮助和/或建议。

代码:

float* vecSub( float *pV0, float *pV1, float *pVRes )
{
    if (pV0 && pV1 && pVRes)
    {
        pVRes[0] = pV0[0] - pV1[0];
        pVRes[1] = pV0[1] - pV1[1];
        pVRes[2] = pV0[2] - pV1[2];
        return pVRes;
    }
    return 0;
}

float vecLength( float *pV )
{
    if(pV) return sqrtf(pV[0]*pV[0]+pV[1]*pV[1]+pV[2]*pV[2]);
    return 0.0f;
}

float vecDistance( float *pV1, float *pV2 )
{
    float fLen=0.0f;

    if(pV1 && pV2)
    {
        float av[4];

        vecSub(pV2, pV1, av);
        fLen=vecLength(av);
    }

    return fLen;
}

if (vecDistance(pPlanet->afPosition, pPlanet1->afPosition) <= pPlanet->fRadius + pPlanet1->fRadius)
{
    //Collision resolution code
}

2 个答案:

答案 0 :(得分:0)

我想您有一个固定的时间步长的离散时间模拟。

您的碰撞检测似乎还不够。如果行星的速度足够高,并且它们的半径足够小,则可能使它们只是彼此通过而不会检测到碰撞。如果碰撞不是正面的,而是切线的,情况就更糟了。

一种更为审慎的方法是不观察行星,而是观察较大半径的球(动态取决于行星的速度),并且当两个碰撞时,切换到更精细的时间步长。

如果您采用这种方式,请仔细将模拟与可视化分开。

答案 1 :(得分:0)

比方说,您使用一个小的结构来描述坐标,而另一个则用于轴对齐的边界框:

typedef struct {
    double  x;
    double  y;
    double  z;
} vec3d;

typedef struct {
    vec3d   min;
    vec3d   max;
} box3d; /* Axis-aligned bounding box */

假设您模拟N球形物体,其半径在double radius[]中描述,当前坐标在vec3d curr[](或vec3d *curr)中,而先前坐标在{{1}中}(或vec3d prev[])。

我们想检查是否有物体碰撞或相交,但是要有效地做到这一点。 为避免进行不必要的工作,请对与轴对齐的边界框使用数组,以使该框在先前和当前坐标处包含球形对象。如果两个对象的轴对齐边界框相交,则两个对象只能碰撞或相交:

vec3d *prev

要计算框,我们取中心坐标,然后按半径展开框。 (与轴对齐的边界框不需要精确;它们只需要覆盖两个位置的球体。如果它们较大,则意味着我们将做一些不必要的工作。我们使用与轴对齐的边界框,因为检查其中两个是否相交非常快。)

如果我们假设两个球形物体在每个时间步中具有恒定速度,则时间步中的时间static inline double dmin(const double d1, const double d2) { return (d1 <= d2) ? d1 : d2; } static inline double dmax(const double d1, const double d2) { return (d1 >= d2) ? d1 : d2; } void update_boxes(box3d *box, const size_t count, const vec3d *curr, const vec3d *prev, const double *radius) { size_t i; for (i = 0; i < count; i++) { box[i].min.x = dmin(curr[i].x, prev[i].x) - radius[i]; box[i].max.x = dmax(curr[i].x, prev[i].x) + radius[i]; box[i].min.y = dmin(curr[i].y, prev[i].y) - radius[i]; box[i].max.y = dmax(curr[i].y, prev[i].y) + radius[i]; box[i].min.z = dmin(curr[i].z, prev[i].z) - radius[i]; box[i].max.z = dmax(curr[i].z, prev[i].z) + radius[i]; } } t)的位置为

0 <= t && t <= 1

这是两个位置之间3D的简单linear interpolationxi(t) = (1-t)*prev[i].x + t*curr[i].x; yi(t) = (1-t)*prev[i].y + t*curr[i].y; zi(t) = (1-t)*prev[i].z + t*curr[i].z; 在上一个时间步,t = 0在当前时间步。

如果我们在索引t = 1i上形成两个这样的对象之间距离平方的方程,我们将得到

k

其中L(t) = SQUARE( ((1-t)*prev[i].x + t*curr[i].x) - ((1-t)*prev[k].x + t*prev[k].x) ) + SQUARE( ((1-t)*prev[i].y + t*curr[i].y) - ((1-t)*prev[k].y + t*prev[k].y) ) + SQUARE( ((1-t)*prev[i].z + t*curr[i].z) - ((1-t)*prev[k].z + t*prev[k].z) ) 。当其导数为零时,它达到最小值。如果我们为SQUARE(expr) = (expr)*(expr)解决了这个问题,则会发现确实存在一个真实根,这意味着沿这些线性路径以恒定速度移动的两个对象在时间t彼此最接近:

t

仅当除数不为零(因为它是平方和,所以永远不能为负)时才有效。

我们只对t = ( (prev[i].x - prev[k].x) * ( (prev[i].x - prev[j].x) - (curr[i].x - curr[k].x) ) + (prev[i].y - prev[k].y) * ( (prev[i].y - prev[j].y) - (curr[i].y - curr[k].y) ) + (prev[i].z - prev[k].z) * ( (prev[i].z - prev[j].z) - (curr[i].z - curr[k].z) ) ) / ( SQUARE( (prev[i].x - prev[k].x) - (curr[i].x - curr[k].x) ) + SQUARE( (prev[i].y - prev[k].y) - (curr[i].y - curr[k].y) ) + SQUARE( (prev[i].z - prev[k].z) - (curr[i].z - curr[k].z) ) ) t >= 0的情况感兴趣;也就是说,在先前和当前时间步长之间两个对象彼此最接近的情况。

如果确实发生了,我们要做的就是将t <= 1重新插入方程式,然后与t进行比较,以找出两个物体是否碰撞。

让我们看一个示例函数,该函数还计算与轴对齐的边界框,将其用于快速选择,并应正确检测碰撞:

SQUARE(radius[i] + radius[k])

从技术上讲,在同一时间步中可以碰撞两个以上的对象,尽管这种情况极为罕见。问题是,如果您在上述循环中“删除”了任何碰撞对象,则可能会错过它们。 (我怀疑这样的情况很少发生,不必担心,但是我是一个偏执的人,并且不喜欢不担心事情。:)

为避免这种情况,我将上述循环中的冲突保存到某种数组或disjoint-set data structure中。然后,在上面的代码之后,合并所有碰撞对象。 (请注意,不相交集数据结构使此操作更容易,因为如果您有碰撞AB,BC和CD,则不相交集实际上会将其解析为AB,AC和AD。否则,如果将B与A结合并销毁, B,将C加入B是不可能的,同样,这是一个极端情况,但不必担心这种极端情况是拥有可靠的模拟器与使每千次模拟崩溃,或者十亿分之一的模拟崩溃的模拟器之间的区别,这是一个无法理解的原因。)