zoukankan      html  css  js  c++  java
  • Bounds(包围盒)概述与AABB包围盒应用

    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.更多其他方法...

    原文连接:https://blog.csdn.net/qq_29579137/article/details/70502591

  • 相关阅读:
    早晨突然想到的几句话
    VBA-工程-找不到工程或库-解决方案
    Mysql 服务无法启动 服务没有报告任何错误
    一道有趣的面试题
    异步和多线程
    异或运算
    线性代数解惑
    全文搜索引擎 Elasticsearch (一)
    HandlerExceptionResolver统一异常处理 返回JSON 和 ModelAndView
    MySQL 20个经典面试题
  • 原文地址:https://www.cnblogs.com/atong/p/14092131.html
Copyright © 2011-2022 走看看