Bounds(包围盒)概述与AABB包围盒应用

本文介绍了包围盒的基本概念及其在Unity中的应用。包括AABB、包围球、OBB和FDH等不同类型的包围盒,并提供了Unity代码示例,如模型包围盒的计算方法和碰撞器的自动添加。

Bounds(包围盒)概述与应用

==转载标明出处==

1.包围盒描述(摘至百度百科):

1.1 什么是包围盒?

    包围盒算法是一种求解离散点集最优包围空间的方法。
    基本思想是用体积稍大且特性简单的几何体(称为包围盒)来近似地代替复杂的几何对象。
    最常见的包围盒算法有AABB包围盒(Axis-aligned bounding box),
    包围球(Sphere), 
    方向包围盒OBB(Oriented bounding box)
    以及固定方向凸包FDH(Fixed directions hulls或k-DOP)。

1.2 包围盒的类型:

    1.2.1 AABB包围盒(Axis-aligned bounding box)

        AABB是应用最早的包围盒。它被定义为包含该对象,且边平行于坐标轴的最小六面体
    。故描述一个AABB,仅需六个标量。AABB构造比较简单,存储空间小,但紧密性差,尤其
    对不规则几何形体,冗余空间很大,当对象旋转时,无法对其进行相应的旋转。处理对象
    是刚性并且是凸的,不适合包含软体变形的复杂的虚拟环境情况。
        AABB也是比较简单的一类包围盒。但对于沿斜对角方向放置的瘦长形对象,其紧密性
    较差。由于AABB相交测试的简单性及较好的紧密性,因此得到了广泛的应用,还可以
    用于软体对象的碰撞检测。

    1.2.2 包围球(Sphere)

        包围球被定义为包含该对象的最小的球体。确定包围球,首先需分别计算组成对象的
    基本几何元素集合中所有元素的顶点的x,y,z坐标的均值以确定包围球的球心,再由球心
    与三个最大值坐标所确定的点间的距离确定半径r。包围球的碰撞检测主要是比较两球间半
    径和与球心距离的大小。

    1.2.3 OBB方向包围盒(Oriented bounding box)

        OBB是较为常用的包围盒类型。它是包含该对象且相对于坐标轴方向任意的最小的长方
    体。OBB最大特点是它的方向的任意性,这使得它可以根据被包围对象的形状特点尽可能紧
    密的包围对象,但同时也使得它的相交测试变得复杂。OBB包围盒比AABB包围盒和包围球更
    加紧密地逼近物体,能比较显著地减少包围体的个数,从而避免了大量包围体之间的相交
    检测。但OBB之间的相交检测比AABB或包围球体之间的相交检测更费时。

    1.2.4 FDH固定方向凸包(Fixed directions hulls或k-DOP)

        FDH(k-DOP)是一种特殊的凸包,继承了AABB简单性的特点,但其要具备良好的空间
    紧密度,必须使用足够多的固定方向。被定义为包含该对象且它的所有面的法向量都取自
    一个固定的方向(k个向量)集合的凸包。FDH比其他包围体更紧密地包围原物体,创建的层
    次树也就有更少的节点,求交检测时就会减少更多的冗余计算,但相互间的求交运算较为复杂。

2.AABB包围盒在Unity中的拓展

以下这段代码可以添加到静态类中作为Transform的拓展方法可以直接使用
这段代码可以直接获取模型的包围盒数据,不论是几个层级的模型,都可以直接获取最大的包围盒
详细计算可以见代码,就不详细讲解了,看不懂可私信我

/// <summary>
/// 获取模型包围盒的中心点
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public static Vector3 CENTER( this Transform model )
{
    Vector3 result = Vector3.zero;
    int counter = 0;
    calculateCenter(model,ref result,ref counter);
    return result / counter;
}


/// <summary>
/// 获取模型包围盒
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public static Bounds BOUNDS( this Transform model )
{
    Vector3 oldPos = model.position;
    model.position = Vector3.zero;
    Bounds resultBounds = new Bounds(model.CENTER() , Vector3.zero);
    calculateBounds(model , ref resultBounds);
    model.position = oldPos;
    Vector3 scalueValue = scaleValue(model); ;
    resultBounds.size = new Vector3(resultBounds.size.x / scalueValue.x , resultBounds.size.y / scalueValue.y , resultBounds.size.z / scalueValue.z);
    return resultBounds;
}

private static void calculateCenter( Transform model , ref Vector3 result , ref int counter )
{
    if (model.childCount.Equals(0))
    {
        if(!model.GetComponent<Renderer>())
            return;
        result += model.center();
        counter++;
        return;
    }
    List<Transform> childModels = model.GetComponentsInChildrenNoSelf<Transform>();
    for (int i = 0; i < childModels.Count; i++, ++counter)
        calculateCenter(childModels[i] , ref result , ref counter);
}

private static Vector3 scaleValue( Transform model )
{
    Vector3 result = model.localScale;
    return calculateScale(model,ref result);
}

private static Vector3 calculateScale( Transform model ,ref Vector3 value)
{
    if (model.parent)
    {
        Vector3 scale = model.parent.localScale;
        value = new Vector3(value.x * scale.x , value.y * scale.y , value.z * scale.z);
        calculateScale(model.parent,ref value);
    }
    return value;
}

private static void calculateBounds( Transform model , ref Bounds bounds )
{
    if (model.childCount.Equals(0))
    {
        if (!model.GetComponent<Renderer>())
            return;
        bounds.Encapsulate(model.bounds());
        return;
    }
    List<Transform> childModels = model.GetComponentsInChildrenNoSelf<Transform>();
    for (int i = 0; i < childModels.Count; i++)
        calculateBounds(childModels[i],ref bounds);
}

3.例子,关于上面拓展方法的使用

这里写图片描述

1.添加碰撞器
AddCollider这个拓展方法可以为模型添加一个最大的Box碰撞器,可以在游戏中需要添加碰撞器的地方使用,省去为每一个模型添加碰撞器带来的性能损耗。

public static Bounds AddCollider( this Transform model )
{
    Bounds bounds = model.BOUNDS();
    BoxCollider collider = model.gameObject.AddComponent<BoxCollider>();
    collider.center = bounds.center;
    collider.size = bounds.size;
    return bounds;
}

2.定位模型
在某些设计中可能需要对模型定位,但是每一个模型的位置会因建模带来误差,因此直接利用模型的位置定位的体验很不好,相机的位置和角度都不好固定,这里我们就可以用包围盒来定位,计算出包围盒,我们可以得到包围盒上的很多点位置,可以利用这些点固定相机的位置,角度也可以利用朝向包围盒的中心来固定

public static void SetPosition()
{
    //todo 详细的定位代码在理解以上内容之后根据自己的需求编写
    //todo 比如你可以把相机的位置固定在上下左右,以及包围盒八个顶点
}

3.更多其他方法...

==转载标明出处==

<think>好的,用户现在问的是关于AABB包围盒的各顶点顺序。之前他们问过如何在VTK C++中获取STL的AABB最大点,现在可能是在进一步理解包围盒的结构,或者想要知道所有顶点的位置,以便进行后续处理,比如可视化、碰撞检测等。 首先,我需要回忆一下AABB的定义。AABB是轴对齐的,所以它的边坐标轴平行,由最小和最大点确定。用户已经知道最大点的获取方法,现在需要知道所有8个顶点的坐标顺序。 接下来,我应该解释每个顶点是怎么由最小和最大坐标组合而成的。通常,AABB的顶点可以通过min和max在x、y、z三个轴上的组合来生成,共有2^3=8种组合方式。需要将这些组合按一定的顺序列出,可能需要按照某种逻辑顺序,比如二进制递增的顺序,或者从某个角开始顺时针排列。 然后,用户可能需要这些顶点的顺序来正确应用,比如绘制包围盒的边或者面。所以,正确的顺序对于连接顶点形成边或面很重要。例如,在绘制线框时,需要知道哪些顶点相连,这时候顶点顺序就关键了。 另外,用户可能没有明确说明他们的具体应用场景,但根据之前的VTK代码,他们可能在处理3D模型,需要包围盒进行空间分析或者可视化。这时候提供顶点顺序可以帮助他们构造包围盒的几何表示。 需要注意的潜在问题是,用户可能混淆了顶点的空间位置和索引顺序,或者不清楚如何从bounds数组中得到所有顶点。因此,需要用清晰的方式列出每个顶点的坐标,并说明每个坐标是如何从min和max值得来的。 此外,可能需要提醒用户,虽然AABB顶点是固定的,但在不同的系统或库中,顶点索引可能有所不同,但基本原理是一致的。因此,重点应放在坐标的组合方式上,而不是特定的索引顺序。 最后,可能需要给出一个具体的例子,比如假设bounds数组中的值,然后展示所有8个顶点的坐标,这样用户更容易理解。同时,可以提到在VTK中如何利用这些顶点来创建多边形数据,比如使用vtkPoints和vtkCellArray来构建包围盒的可视化。 总结一下,回答需要包括: 1. AABB顶点由min和max组合生成,共8个。 2. 每个顶点的坐标组合方式。 3. 顶点的顺序或索引方式,可能按二进制顺序排列。 4. 提供一个具体的例子,帮助理解。 5. 可能的应用场景或如何在VTK中进一步使用这些顶点。</think>--- 在三维空间中,**AABB包围盒(轴对齐包围盒)** 是一个由最小点 $(x_{\text{min}}, y_{\text{min}}, z_{\text{min}})$ 和最大点 $(x_{\text{max}}, y_{\text{max}}, z_{\text{max}})$ 定义的立方体,其 **8个顶点** 的坐标由这两个点的组合构成。顶点顺序通常按以下方式排列: --- ### **顶点顺序定义** 假设已通过 `GetBounds()` 获取边界数组 `bounds[6]`: - `bounds[0] = x_min`, `bounds[1] = x_max` - `bounds[2] = y_min`, `bounds[3] = y_max` - `bounds[4] = z_min`, `bounds[5] = z_max` 则所有顶点坐标如下(按二进制索引顺序排列): | 顶点索引 | 坐标 $(x, y, z)$ | 组合描述 | |----------|------------------------------------------|--------------------| | 0 | $(x_{\text{min}}, y_{\text{min}}, z_{\text{min}})$ | 最小点(左下后) | | 1 | $(x_{\text{max}}, y_{\text{min}}, z_{\text{min}})$ | 右下后 | | 2 | $(x_{\text{max}}, y_{\text{max}}, z_{\text{min}})$ | 右上前 | | 3 | $(x_{\text{min}}, y_{\text{max}}, z_{\text{min}})$ | 左上前 | | 4 | $(x_{\text{min}}, y_{\text{min}}, z_{\text{max}})$ | 左下前 | | 5 | $(x_{\text{max}}, y_{\text{min}}, z_{\text{max}})$ | 右下前 | | 6 | $(x_{\text{max}}, y_{\text{max}}, z_{\text{max}})$ | 最大点(右上前) | | 7 | $(x_{\text{min}}, y_{\text{max}}, z_{\text{max}})$ | 左上前 | --- ### **可视化顶点顺序** 顶点通常按以下顺序连接形成包围盒的边(例如用`vtkCellArray`绘制线框): ```plaintext 边连接顺序: 0→1→2→3→0 (底面) 4→5→6→7→4 (顶面) 0→4, 1→5, 2→6, 3→7 (侧边) ``` --- ### **示例** 若 `bounds = [0, 2, -1, 1, 3, 5]`,则顶点坐标为: 1. $(0, -1, 3)$ 2. $(2, -1, 3)$ 3. $(2, 1, 3)$ 4. $(0, 1, 3)$ 5. $(0, -1, 5)$ 6. $(2, -1, 5)$ 7. $(2, 1, 5)$ 8. $(0, 1, 5)$ --- ### **在VTK中应用** 若需生成AABB的几何体(如绘制包围盒),可参考以下代码片段: ```cpp vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // 添加所有顶点 points->InsertNextPoint(bounds[0], bounds[2], bounds[4]); // 0 points->InsertNextPoint(bounds[1], bounds[2], bounds[4]); // 1 // ... 依次插入其他顶点 // 定义边的连接关系(按上述顺序) lines->InsertNextCell({{0,1,2,3,0}}); // 底面 lines->InsertNextCell({{4,5,6,7,4}}); // 顶面 // ... 添加其他边 // 创建PolyData并渲染 vtkSmartPointer<vtkPolyData> boxPolyData = vtkSmartPointer<vtkPolyData>::New(); boxPolyData->SetPoints(points); boxPolyData->SetLines(lines); ``` --- ### **关键点** - **轴对齐特性**:顶点坐标直接由坐标轴的最小/最大值组合生成,无需旋转或复杂计算。 - **应用场景**:用于快速碰撞检测、空间查询、模型裁剪等。 - **扩展**:若需要方向敏感的包围盒(OBB),需使用 `vtkOBBTree` 计算主轴和变换矩阵。
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值