如何最有效地对一组球体执行碰撞检测

时间:2013-05-28 00:15:59

标签: c++ collision

假设我有一个带有多个核心的CPU,我想在其上找到哪些球体正在接触。每个球体连接的任何球体集合(即,它们都触及集合中的至少一个球体)被称为“组”,并且被组织成一个向量,在下面的示例中称为“group_members” ”。为了达到这个目的,我目前正在使用一种相当昂贵的操作,概念上看起来像这样:

vector<Sphere*> unallocated_spheres = all_spheres; // start with a copy of all spheres
vector<vector<Sphere*>> group_sequence; // groups will be collected here

while (unallocated_spheres.size() > 0U) // each iteration of this will represent the creation of a new group
{
    std::vector<Sphere*> group_members; // this will store all members of the current group
    group_members.push_back(unallocated_spheres.back()); // start with the last sphere (pop_back requires less resources than erase)
    unallocated_spheres.pop_back(); // it has been allocated to a group so remove it from the unallocated list

    // compare each sphere in the new group to every other sphere, and continue to do so until no more spheres are added to the current group
    for (size_t i = 0U; i != group_members.size(); ++i) // iterators would be unsuitable in this case
    {
        Sphere const * const sphere = group_members[i]; // the sphere to which all others will be compared to to check if they should be added to the group
        auto it = unallocated_spheres.begin();
        while (it != unallocated_spheres.end())
        {
            // check if the iterator sphere belongs to the same group
            if ((*it)->IsTouching(sphere))
            {
                // it does belong to the same group; add it and remove it from the unallocated_spheres vector and repair iterators
                group_members.push_back(*it);
                it = unallocated_spheres.erase(it); // repair the iterator
            }
            else ++it; // if no others were found, increment iterator manually
        }
    }

    group_sequence.push_back(group_members);
}

有人建议在壁挂时间方面提高此代码的效率吗?我的程序花费了大部分时间来完成这些循环,并且任何有关如何在结构上改变它以使其更高效的建议都将受到赞赏。

请注意,由于这些是球体,“IsTouching()”是一种非常快速的浮点运算(比较两个球体的位置和半径)。它看起来像这样(注意x,y和z是该欧氏维度中球体的位置):

// input whether this cell is touching the input cell (or if they are the same cell; both return true)
bool const Sphere::IsTouching(Sphere const * const that) const
{
    // Apply pythagoras' theorem in 3 dimensions
    double const dx = this->x - that->x;
    double const dy = this->y - that->y;
    double const dz = this->z - that->z;

    // get the sum of the radii of the two cells
    double const rad_sum = this->radius + that->radius;

    // to avoid taking the square root to get actual distances, we instead compare 
    // the square of the pythagorean distance with the square of the radii sum
    return dx*dx + dy*dy + dz*dz < rad_sum*rad_sum;
}

1 个答案:

答案 0 :(得分:1)

  

有人建议在壁挂时间方面提高此代码的效率吗?

更改算法。低级优化对您没有帮助。 (尽管如果将group_members循环移到while之外,您将获得非常小的加速)

您需要使用空间分区(bsp-tree,oct-tree)或扫描和修剪算法。

Sweep and prune(维基百科有原始文章的链接,加上你可以google它)可以轻松处理单核机器上的100000个移动和可能碰撞的球体(好吧,只要你不把它们全部放在相同的坐标)并且比空间分区更容易实现。如果您知道碰撞对象的最大可能大小,则扫描和修剪将更适合/更简单地实现。

如果您要使用扫描和修剪算法,您应该学习insertion sort算法。当您处理“几乎”排序的数据时,这种排序算法比任何其他算法都要快,这就是sweep-and-prune的情况。当然,你还需要一些quicksort或heapsort的实现,但是标准库提供了这个。