两球碰撞的充要条件是球心平方距离 ≤ 半径和的平方;直接比较平方距离可避免sqrt开方运算的性能损耗与浮点误差风险。

判断球心距离是否小于半径之和
两个三维球体碰撞的充要条件是:球心之间的欧氏距离 ≤ 两球半径之和。这是最直接、最高效的判断方式,不需要任何额外几何计算。
关键点在于避免开方运算带来的性能损耗和精度风险——直接比较平方距离与半径和的平方即可。
-
std::sqrt不必要,且浮点误差可能在边界处导致误判 - 用
(x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + (z1-z2)*(z1-z2)计算平方距离 - 比较
squared_distance ,全部为浮点或全部为 double 更安全 - 若球体半径为
float类型,建议统一转为double再计算,防止溢出或精度丢失
注意浮点数比较的精度陷阱
当两球几乎相切时,squared_distance 和 (r1 + r2) * (r1 + r2) 的差值可能落在浮点误差范围内,直接用 == 或 可能不可靠。
更稳健的做法是引入一个小的容差值(epsilon),但要注意:容差不能简单套用固定值(如 1e-6),而应与数值量级匹配。
立即学习“C++免费学习笔记(深入)”;
- 推荐使用相对容差:
abs(a - b) - 若已知半径和坐标范围较稳定(例如都在 [0, 1000] 内),可设
const double eps = 1e-9;并写成squared_distance - 不要对
squared_distance和(r1 + r2) * (r1 + r2)分别取 sqrt 后再减——这会放大误差
结构体封装与内联函数建议
实际项目中,把球体抽象为结构体并提供内联碰撞检测函数,既清晰又零开销。
struct Sphere {
double x, y, z, r;
};
<p>inline bool intersects(const Sphere& a, const Sphere& b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
double dz = a.z - b.z;
double r_sum = a.r + b.r;
return dx<em>dx + dy</em>dy + dz<em>dz <= r_sum </em> r_sum;
}</p>- 参数用
const Sphere&避免拷贝 - 函数声明为
inline,现代编译器通常能自动内联,但显式标注有助于调试构建下的行为可预测 - 不建议在结构体内重载
operator==来表示“碰撞”,语义混淆且无法泛化
性能敏感场景下的提前退出优化
如果需要批量检测大量球对(如物理引擎中的粗筛阶段),可先用轴对齐包围盒(AABB)做快速排除,再对潜在碰撞对调用球体精确检测。
但对单次判断而言,AABB 额外计算反而更慢——因为要比较六个坐标分量,而球体只需三次减法、三次乘加和一次比较。
- 仅当
r1或r2为 0(退化为点)时,可省去一次乘法,但分支预测失败成本可能更高,通常不值得特判 - 若坐标以
std::array<double></double>存储,可用循环展开或 SIMD 加速,但对单次调用意义不大 - 真正影响性能的是内存布局:确保
Sphere实例连续存放(如std::vector<sphere></sphere>),利于缓存预取
实际写的时候,最容易被忽略的是半径符号——如果输入可能含负值,必须在调用前做 abs(r) 或断言检查;否则 r1 + r2 可能异常小,导致误报碰撞。









