计算空间物体包围球的两种算法实现

简介: 计算空间物体包围球的两种算法实现

计算空间物体包围球的两种算法实现

1. 概述

在进行二维空间几何运算的之前,往往会用包围盒进行快速碰撞检测,从而筛掉一些无法碰撞到的可能。而在三维中,比较常用的就是包围球了。当然,如何计算包围球是一个问题。

2. 详论

2.1. naive算法

一个最简单的思路就是,计算空间顶点在X、Y、Z方向上的最大值和最小值,那么就可以得到8个顶点组成的包围盒。取包围球中心为包围盒中心点,而包围球半径有的人认为可以取中心点到八个顶点的最大距离——这样其实并不严密。最好还是计算中心点到所有顶点距离的最大值:

void BoundingSphere::GetBoundingSphereNative(const std::vector<Vec3d>& pointList)
{
    if (pointList.empty())
    {
        return;
    }
    Vec3d minPoint(DBL_MAX, DBL_MAX, DBL_MAX);
    Vec3d maxPoint(-DBL_MAX, -DBL_MAX, -DBL_MAX);
    size_t vertexCount = pointList.size();
    for (size_t vi = 0; vi < vertexCount; vi++)
    {
        if (minPoint.x() > pointList[vi].x())
        {
            minPoint.x() = pointList[vi].x();
        }
        if (minPoint.y() > pointList[vi].y())
        {
            minPoint.y() = pointList[vi].y();
        }
        if (minPoint.z() > pointList[vi].z())
        {
            minPoint.z() = pointList[vi].z();
        }
        if (maxPoint.x() < pointList[vi].x())
        {
            maxPoint.x() = pointList[vi].x();
        }
        if (maxPoint.y() < pointList[vi].y())
        {
            maxPoint.y() = pointList[vi].y();
        }
        if (maxPoint.z() < pointList[vi].z())
        {
            maxPoint.z() = pointList[vi].z();
        }
    }
    Vec3d naiveCenter = (maxPoint + minPoint) / 2;
    double naiveRadius = 0;
    for (size_t vi = 0; vi < vertexCount; vi++)
    {
        naiveRadius = std::max(naiveRadius, (pointList[vi] - naiveCenter).length());
    }
    data = { naiveCenter.x(), naiveCenter.y(), naiveCenter.z(), naiveRadius };
}
CPP 折叠 复制 全屏

这个算法的思路比较简单,所以称之为naive算法。

2.2. ritter算法

另外一种算法是一个名为ritter提出来的,所以称为ritter算法。

首先计算出X方向上距离最远的两个点,Y方向上距离最远的两个点以及Z方向上距离最远的两个点。以这三个距离最远的范围作为初始直径,这三个距离的中心点作为初始球心。

然后依次遍历所有点,判断点是否在这个包围球内。如果不在,则更新包围球。如下图所示:

1723605256692.png

最后将之前的球心O移动向量OS��→,就是新的包围球的球心位置了。

具体的算法代码实现:

void BoundingSphere::GetBoundingSphereRitter(const std::vector<Vec3d>& pointList)
{
    //
    Vec3d minPoint(DBL_MAX, DBL_MAX, DBL_MAX);
    Vec3d maxPoint(-DBL_MAX, -DBL_MAX, -DBL_MAX);
    size_t minX = 0, minY = 0, minZ = 0;
    size_t maxX = 0, maxY = 0, maxZ = 0;
    size_t vertexCount = pointList.size();
    for (size_t vi = 0; vi < vertexCount; vi++)
    {
        if (minPoint.x() > pointList[vi].x())
        {
            minPoint.x() = pointList[vi].x();
            minX = vi;
        }
        if (minPoint.y() > pointList[vi].y())
        {
            minPoint.y() = pointList[vi].y();
            minY = vi;
        }
        if (minPoint.z() > pointList[vi].z())
        {
            minPoint.z() = pointList[vi].z();
            minZ = vi;
        }
        if (maxPoint.x() < pointList[vi].x())
        {
            maxPoint.x() = pointList[vi].x();
            maxX = vi;
        }
        if (maxPoint.y() < pointList[vi].y())
        {
            maxPoint.y() = pointList[vi].y();
            maxY = vi;
        }
        if (maxPoint.z() < pointList[vi].z())
        {
            maxPoint.z() = pointList[vi].z();
            maxZ = vi;
        }
    }
    //
    double maxLength2 = (pointList[maxX] - pointList[minX]).length2();
    Vec3d min = pointList[minX];
    Vec3d max = pointList[maxX];
    {
        double yMaxLength2 = (pointList[maxY] - pointList[minY]).length2();
        if (maxLength2 < yMaxLength2)
        {
            maxLength2 = yMaxLength2;
            min = pointList[minY];
            max = pointList[maxY];
        }
        double zMaxLength2 = (pointList[maxZ] - pointList[minZ]).length2();
        if (maxLength2 < zMaxLength2)
        {
            maxLength2 = zMaxLength2;
            min = pointList[minZ];
            max = pointList[maxZ];
        }
    }
    //
    Vec3d ritterCenter = (min + max) / 2;
    double ritterRadius = sqrt(maxLength2) / 2;
    for (size_t i = 0; i < vertexCount; i++)
    {
        Vec3d d = pointList[i] - ritterCenter;
        double dist2 = d.length2();
        if (dist2 > ritterRadius * ritterRadius)
        {
            double dist = sqrt(dist2);
            double newRadious = (dist + ritterRadius) * 0.5;
            double k = (newRadious - ritterRadius) / dist;
            ritterRadius = newRadious;
            Vec3d temp = d * k;
            ritterCenter = ritterCenter + temp;
        }
    }
    data = { ritterCenter.x(), ritterCenter.y(), ritterCenter.z(), ritterRadius };
}

2.3. 其他

理论上来说,ritter算法的实现要优于naive算法,能够得到更加贴合的包围球。当然理论只是理论,具体的实现还要看最终的效果。根据文献2中所说,经过Cesium的比对测试,19%的情况下,ritter算法的效果比naive算法差;11%的情况下,ritter算法的效果会比naive算法好。所以在Cesium中,包围球的实现是把两者都实现了一遍,然后取半径较小的结果。

3. 参考

  1. 3D空间包围球(Bounding Sphere)的求法
  2. Cesium原理篇:3最长的一帧之地形(2:高度图)

分类: 计算几何

标签: 包围球 , 计算几何 , 算法




相关文章
|
2月前
|
存储 分布式计算 算法
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
63 0
|
2月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
68 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
3月前
|
算法 数据可视化 数据安全/隐私保护
基于LK光流提取算法的图像序列晃动程度计算matlab仿真
该算法基于Lucas-Kanade光流方法,用于计算图像序列的晃动程度。通过计算相邻帧间的光流场并定义晃动程度指标(如RMS),可量化图像晃动。此版本适用于Matlab 2022a,提供详细中文注释与操作视频。完整代码无水印。
|
3月前
|
算法 C++
如何精确计算出一个算法的CPU运行时间?
如何精确计算出一个算法的CPU运行时间?
|
4月前
|
算法 Go Python
[算法]计算斐波拉契数列
[算法]计算斐波拉契数列
|
4月前
|
算法 C++
空间中判断点在三角形内算法(方程法)
空间中判断点在三角形内算法(方程法)
61 0
|
4月前
|
算法
空间点与直线距离算法
空间点与直线距离算法
55 0
|
4月前
|
算法 C++
空间直线与球面相交算法
空间直线与球面相交算法
33 0
|
6月前
|
机器学习/深度学习 算法
**反向传播算法**在多层神经网络训练中至关重要,它包括**前向传播**、**计算损失**、**反向传播误差**和**权重更新**。
【6月更文挑战第28天】**反向传播算法**在多层神经网络训练中至关重要,它包括**前向传播**、**计算损失**、**反向传播误差**和**权重更新**。数据从输入层流经隐藏层到输出层,计算预测值。接着,比较预测与真实值计算损失。然后,从输出层开始,利用链式法则反向计算误差和梯度,更新权重以减小损失。此过程迭代进行,直到损失收敛或达到训练次数,优化模型性能。反向传播实现了自动微分,使模型能适应训练数据并泛化到新数据。
70 2
|
6月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题