两个球体重叠或包含的判断依据是球心距离平方与半径和、差平方的关系:distSq ≤ (rA+rB)² 且 distSq ≥ (rA−rB)² 时重叠;distSq < (rA−rB)² 时内含;distSq > (rA+rB)² 时分离。

判断两个球体是否重叠或包含的核心逻辑
两个三维球体是否重叠或包含,只取决于球心距离与半径之和、半径之差的关系。不需要任何几何库或数值求解——纯代数判断,稳定、零误差、常数时间。
设球 A 球心为 centerA,半径为 rA;球 B 球心为 centerB,半径为 rB。先计算球心间欧氏距离的平方(避免开方):distSq = (x2−x1)² + (y2−y1)² + (z2−z1)²。
- 若
distSq > (rA + rB) * (rA + rB)→ 两球分离(无重叠) - 若
distSq (且假设 <code>rA >= rB)→ B 完全在 A 内部(包含) - 否则 → 存在非空交集(重叠:相交或内切)
用 std::sqrt 还是直接比平方?
必须用平方比较。调用 std::sqrt 不仅慢(尤其在循环中高频调用),还引入浮点误差风险——比如当 distSq 恰好等于 (rA + rB)² 时,std::sqrt(distSq) 可能因舍入略小于理论值,导致误判为“重叠”而非“外切”。
实际写法应始终基于平方:
立即学习“C++免费学习笔记(深入)”;
float dx = centerB.x - centerA.x; float dy = centerB.y - centerA.y; float dz = centerB.z - centerA.z; float distSq = dx*dx + dy*dy + dz*dz; float rSum = rA + rB; float rDiff = std::abs(rA - rB); // 注意取绝对值,避免负数平方出错 bool overlap = distSq <= rSum * rSum; bool contains = distSq <= rDiff * rDiff;
浮点精度下 == 和 的取舍
几何判断中,“相切”(外切或内切)属于重叠/包含的边界情况,业务上通常需要包含它。因此一律用 ,而不是 <code> 或 <code>==。
- 外切(
distSq == (rA + rB)²)→ 属于“重叠”,因为交集是一个点,测度为零但拓扑非空 - 内切(
distSq == (rA − rB)²,且rA > rB)→ 属于“包含”,因为小球表面刚好接触大球内壁 - 用
==判断相切极易失败:浮点运算累积误差会让本该相等的值差一个 ulp
结构体设计与常见坑
若用自定义结构体表示球(如 struct Sphere { glm::vec3 center; float radius; }),注意 radius 必须非负。运行时未校验负半径会导致 rDiff * rDiff 计算失真(例如 rA = 2, rB = -1 时 std::abs(rA - rB) 是 3,但负半径无几何意义)。
- 构造函数或 setter 中强制
radius = std::max(0.0f, radius) - 避免用
glm::distance直接算距离再比较——它内部调用了sqrt,多一次开方,且无法复用distSq同时判断重叠和包含 - 若球坐标来自用户输入或文件解析,检查 NaN/Inf:对
center分量或radius做std::isnan()和std::isinf()判定,否则distSq可能为 NaN,后续比较全部返回 false
浮点数的符号位、次正规数、溢出边界这些细节不会出现在公式里,但一旦球心距离极大或半径极小,dx*dx 就可能上溢成 inf,导致后续比较失效——这种边缘情况在仿真或大规模场景中真会出现。


















