如何在一组球体上最有效地执行碰撞检测
How do I most efficiently perform collision detection on a group of spheres
假设我有一个有几个内核的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);
}
有没有人对提高这段代码在wall time方面的效率有什么建议?我的程序花费了相当一部分时间来运行这些循环,任何关于如何在结构上改变它以使其更有效的建议都将不胜感激。
请注意,由于这些是球体,"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;
}
有没有人对提高这段代码在wall time方面的效率有什么建议?
修改算法。低级优化对您没有帮助。(尽管如果您将group_members
移出while
循环,您将获得非常小的加速)
您需要使用空间分区(bsp-tree, oct-tree)或扫描和修剪算法。
扫描和修剪(维基百科有原始文章的链接,另外你可以谷歌它)可以很容易地处理100000个移动和可能碰撞的球体在单核机器上(好吧,只要你不把它们都放在相同的坐标上),并且比空间分区更容易实现。如果你知道碰撞对象的最大可能大小,那么扫描和修剪将更适合/更容易实现。
如果你要使用扫描和修剪算法,你应该学习插入排序算法。当您处理"几乎"排序的数据时,这种排序算法比几乎任何其他算法都要快,这就是扫描和修剪的情况。当然,您还需要一些快速排序或堆排序的实现,但是标准库提供了这些。
相关文章:
- 有效地使用std::unordered_map来插入或增加键的值
- 落砂模拟碰撞检测C++和SFML
- 如何有效地在 std::vector 中插入一对?
- 有效地计算多维数组的累积和?
- 如何有效地计算将单位立方体映射到自身的反射和旋转?
- 有效地将大数存储为 2 的幂用于路径问题
- 如何在C++中写入 1000 个文件时有效地缓冲
- 如何有效地找到数组中三元组和的最小差异?
- 如何在C++中有效地将数字值重新分配给字符数组
- C++有效地找到向量中第一个最接近的匹配值?
- 如何有效地操作满足给定谓词的向量中的所有项目?
- 有效地将数据加载到 std::vector 中<char>
- 如何在使用 cin 请求 int 时有效地使用户输入万无一失?
- C++:有效地将Sha256摘要放入OpenSSL Bignum?
- 如何有效地收集给定数组中的重复元素?
- 如何有效地修剪和合并四叉树中的节点?
- 如何有效地检测4个整数变量中的对称性
- 我如何有效地清理此程序并仍然检测文件输入何时不是字母
- 如何在一组球体上最有效地执行碰撞检测
- Box2d奇怪地不检测碰撞