Unity的Bounds(包围盒)简记

news2024/10/5 17:21:35

Unity的Bounds(包围盒)简记

    • 一、Bounds(包围盒)概述
      • 1.什么是包围盒?
      • 2.包围盒的类型
        • 2.1 AABB包围盒(Axis-aligned bounding box)
        • 2.2 包围球(Sphere)
        • 2.3 OBB方向包围盒(Oriented bounding box)
        • 2.4 FDH固定方向凸包(Fixed directions hulls或k-DOP)
        • 2.5 包围盒选择
    • 二、Unity中的Bounds
      • 1.Bounds结构体
        • 1.1 Public Attribute(公告属性)
        • 1.2 Public Functions(公告函数)
    • 三、旋转对Bounds的影响
    • 四、Bounds和碰撞器Collider的区别
    • 参考连接

一、Bounds(包围盒)概述

1.什么是包围盒?

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

2.包围盒的类型

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

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

2.2 包围球(Sphere)

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

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

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

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

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

2.5 包围盒选择

任何实时三维交互式程序,如果没有碰撞检测,都是没有价值,甚至无法使用的。游戏中最常用的碰撞检测技术莫过于包围盒(bounding volume)碰撞检测。对于以60pfs运行的游戏来说,处理每一帧数据的时间只有0.0167s左右,对于不同的游戏,碰撞检测大概需要占10~30%的时间,也就是说,所有碰撞检测必须在0.002~0.005s内完成,非常巨大的挑战。
因此,任何包围盒都应该满足以下特性:

  1. 快速的碰撞检测;
  2. 能紧密覆盖所包围的对象;
  3. 包围盒应该非常容易计算;
  4. 能方便的旋转和变换坐标;
  5. 低内存占用。

最常见的包围盒有:Sphere,AABB,OBB等,外加一个比较特殊的frustum。Sphere能很好的满足1,3,4,5条,但通常包含了太多无用的空间,容易导致错误的碰撞结果。AABB应该是sphere与obb之间的解决方案,同时兼顾了效率和空间覆盖范围。OBB是三者中精度最高的,但检测代价也是最高的。
最终使用哪一种包围盒,是一个非常痛苦的过程,我们需要在效率和精度之间做出权衡取舍。前几天刚好完成了基本的碰撞检测函数,以下是我的一些测试数据,在一定程度上可以作为参考。纯C#代码实现,没有任何GPU加速,单线程在Q6600上运行。

  • AABB包围盒(Axis-aligned bounding box):100万次测试,1000次碰撞,耗时0.014s。
  • 包围球(Sphere):100万次测试,大约有16000次碰撞,耗时0.016s。
  • OBB方向包围盒(Oriented bounding box):使用传统的separate axis算法,100万次测试,30万次碰撞,耗时0.160s左右。对于没有碰撞的情况,几乎在前6条轴的检测中,就能结束检测,也就是说大约50万次(50%)测试都在检测第七条轴之前结束。
  • Vertical-agliened OBB:普通OBB的特殊版本,只能绕Y轴旋转。100w次测试,同样30万次碰撞,耗时0.08s,几乎比普通OBB快了一倍。
  • Frustum-AABB:使用<< Optimized View Frustum Culling Algorithms for Bounding Boxes >>中的算法,100w次测试,6万次碰撞,耗时0.096s。目前我计算n-vertex和p-vertex的方法是瓶颈,大约0.016s的时间花在计算这两个点上。 相比XNA中的BoundingFrustum.Intersects,同样的测试需要0.5s左右。

(以上均为对随机数据的测试,因此不同包围盒之间的实际碰撞次数并没有可比性,也不代表不同类型间的精度)
显然,AABB是性价比最高的,OBB虽然有较高精度,但相对其计算代价来说,并不划算,可以考虑用多个AABB来近似OBB,或者使用代价相对较低的Vertical-agliened OBB。Sphere看起来简单,但计算涉及到开方(虽然Math.Sqrt会直接编译为fsqrt指令),因此仍然没有AABB快(只需要6条逻辑比较指令)

二、Unity中的Bounds

1.Bounds结构体

unity api对Bounds的解释:

An axis-aligned bounding box, or AABB for short, is a box aligned with coordinate axes and fully enclosing some object. Because the box is never rotated with respect to the axes, it can be defined by just its center and extents, or alternatively by min and max points.

翻译:

  • 轴对齐边框(简称AABB)是与坐标轴对齐并完全包围某个对象的框。 因为方框不会相对于坐标轴旋转,所以可以通过它的中心和范围来定义它,或者通过最小和最大点来定义它。

Unity用Bounds这个结构体struct来描述AABB包围盒,获取一个物体AABB包围盒的API有三种:RenderColliderMesh

  • Render:GetComponent<Renderer>().bounds—世界坐标
  • Collider:GetComponent<Collider>().bounds—世界坐标
  • Mesh:GetComponent<MeshFilter>().bounds—本地坐标

    把Mesh.bounds本地坐标换算成世界坐标bounds:

    var centerPoint = transform.TransformPoint(bounds.center);
    Bounds newBounds = new Bounds(centerPoint, bounds.size);
    

注意:不管是2D还是3D碰撞以及精灵和都是有bounds属性的。
总结:幸亏有了这个结构体,在实际开发中适当的使用包围盒会省去很多麻烦,做为结构体他和Vector3一样是不允许为空的。在一些属性中他是只读的。

1.1 Public Attribute(公告属性)

  • center:边界盒的中心(世界坐标);
  • extents:边界框的范围,总是size的一半;
  • max:(世界坐标)边界盒的最大点,这个值总是等于center+extents;
  • min:(世界坐标)边界盒的最小点,这个值总是等于center-extents;
  • size:边界盒的总大小。

我们不能直接修改Bounds结构体里头的centersize属性都不能直接设置。
我们通过画线的方式分别看一下各个参数在预制体上的位置。

Bounds bounds = this.GetComponent<Collider>().bounds;
Debug.DrawLine(bounds.center, bounds.center + bounds.extents, Color.red);


我们可以看到这个向量的长度是从中心点到右上角的长度。
由于extentssize的一半,所以我们这样画size

//得到左下角的位置
Vector3 p1 = bounds.center - bounds.extents;
Debug.DrawLine(p1, p1 + bounds.size, Color.green);


我们可以看到size的长度刚好是从左下角到右上角的长度。
然后我们分别画出最小值和最大值和中心点的连线。

Debug.DrawLine(bounds.center,  bounds.min, Color.gray);
Debug.DrawLine(bounds.center, bounds.max, Color.cyan);


由此可以看出最小值在左下角,最大值在右上角。

1.2 Public Functions(公告函数)

  • Encapsulate:重新计算最大最小点;
  • Contains:可判断点是否包含在边界框内(世界坐标)如我们需判断你是否点击了某个精灵则可以用Contains()
  • SetMinMax:设置边界盒的最小最大值;
  • SqrDistance:点和该边界盒之间的最小平方距离;
  • IntersectRay:射线与改边界盒相交吗?
  • Intersects:与另一个边界相交吗?比如我们需要判断两个精灵是否有重叠在一起则就可以使用Intersects()

三、旋转对Bounds的影响

我们对上面的小方块旋转:

旋转之后我们发现这个最小值最大值不再是小方块的左下角右上角
换言之他并不是和自身的坐标轴对齐
于是,我尝试着画出这个小方块的Bounds:

//后左下角
Vector3 backBottomLeft = bounds.min;
///后右下角
Vector3 backBottomRight = backBottomLeft + new Vector3(bounds.size.x, 0, 0);
///前左下角
Vector3 forwardBottomLeft = backBottomLeft + new Vector3(0, 0, bounds.size.z);
///前右下角
Vector3 forwardBottomRight = backBottomLeft + new Vector3(bounds.size.x, 0, bounds.size.z);
///后右上角
Vector3 backTopRight = backBottomLeft + new Vector3(bounds.size.x, bounds.size.y, 0);
///前左上角
Vector3 forwardTopLeft = backBottomLeft + new Vector3(0, bounds.size.y, bounds.size.z);
///后左上角
Vector3 backTopLeft = backBottomLeft + new Vector3(0, bounds.size.y, 0);
///前右上角
Vector3 forwardTopRight = bounds.max;

Debug.DrawLine(bounds.min, backBottomRight, Color.red);
Debug.DrawLine(backBottomRight, forwardBottomRight, Color.red);
Debug.DrawLine(forwardBottomRight, forwardBottomLeft, Color.red);
Debug.DrawLine(forwardBottomLeft, bounds.min, Color.red);

Debug.DrawLine(bounds.min, backTopLeft, Color.red);
Debug.DrawLine(backBottomRight, backTopRight, Color.red);
Debug.DrawLine(forwardBottomRight, bounds.max, Color.red);
Debug.DrawLine(forwardBottomLeft, forwardTopLeft, Color.red);

Debug.DrawLine(backTopRight, backTopLeft, Color.red);
Debug.DrawLine(backTopLeft, forwardTopLeft, Color.red);
Debug.DrawLine(forwardTopLeft, bounds.max, Color.red);
Debug.DrawLine(bounds.max, backTopRight, Color.red);

运行结果如下:
当小方块完全不做旋转时,本地坐标轴和世界坐标轴重合

旋转45度之后

注:红框是我们画出的小方块的Bounds,
我们发现小方块的Bounds没有随着小方块旋转,但是它仍然完全包裹着小方块
即是:它是与世界坐标轴对齐,完全包围的对象是它自身的预制体

四、Bounds和碰撞器Collider的区别

  • 碰撞器Collider的方框始终跟着模型旋转移动,缩放跟着模型的,只要模型不缩放它也不缩放。
    属于obb包围盒:他是有向的;检测精度较好
  • Bounds跟随模型移动,而不会跟模型着旋转,而是随着模型旋转而缩放变大变小,始终包裹模型。
    属于aabb包围盒:他是无向的;检测精度较差

参考连接

  1. https://blog.csdn.net/SmillCool/article/details/126708371
  2. https://blog.csdn.net/weixin_42977419/article/details/100045078
  3. https://blog.csdn.net/cscscsliqi/article/details/80405059
  4. https://blog.csdn.net/Windgs_YF/article/details/87884884
  5. https://blog.csdn.net/u013628121/article/details/128117992
  6. https://blog.csdn.net/sinat_25415095/article/details/104588989

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/173648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云服务器部署前后端分离项目(若依)详细教程

第一次在Linux云服务器上部署前后端分离项目&#xff0c;查了很多资料和视频&#xff0c;踩了许多坑。成功实现部署若依的前后端分离项目后&#xff0c;想记录一下前后端部署的过程&#xff0c;供学习的小伙伴参考。 目录1. 环境准备2. 开放端口3. 下载前后端项目4. 前端部署5.…

Linux部署Kafka及常见问题记录

Linux部署Kafka及常见问题记录kafka 使用场景Kafka 基本概念BrokerTopic(主题)Partition(分区)ProducerConsumerConsumer Group&#xff08;消费者群组&#xff09;offset 偏移量Linux 安装&启动 kafka修改核心配置文件创建数据存放目录启动验证 kafk 是否启动成功Topic (主…

AX7A200教程(1):DDR3仿真平台搭建(一)

本章节主要调用官方的MIG控制器&#xff0c;并使用官方的MIG控制器进行仿真&#xff0c;开发环境vivado2020.1鉴于很多童鞋无法仿真自己新建的DDR工程&#xff0c;即使使用modelsim仿真也仿真失败&#xff0c;本例程着重在vivado中对自己新建的带DDR3的工程进行仿真。新建DDR3工…

Python SciPy 插值及其他各种插值法

SciPy 插值什么是插值&#xff1f;在数学的数值分析领域中&#xff0c;插值&#xff08;英语&#xff1a;interpolation&#xff09;是一种通过已知的、离散的数据点&#xff0c;在范围内推求新数据点的过程或方法。简单来说插值是一种在给定的点之间生成点的方法。例如&#x…

【MySQL】MyCAT入门综述◆掌握MyCAT的基础概念、功能及适用场景

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/Liunx内核/C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1f4…

二叉树知识概括锦囊(一)

作者&#xff1a;爱塔居 专栏&#xff1a;数据结构 作者简介&#xff1a;大三学生&#xff0c;希望跟大家一起进步&#xff01; 文章目录 目录 文章目录 一、树形结构 二、树的基础知识 三、二叉树 3.1 概念 3.2 特殊的二叉树 3.3 二叉树的性质 四、习题挑战 一、树形结构 树是…

【论文速递】IJCV2022 - CRCNet:基于交叉参考和区域-全局条件网络的小样本分割

【论文速递】IJCV2022 - CRCNet:基于交叉参考和区域-全局条件网络的小样本分割 【论文原文】&#xff1a;CRCNet: Few-shot Segmentation with Cross-Reference and Region-Global Conditional Networks 获取地址&#xff1a;https://link.springer.com/article/10.1007/s112…

BACnet协议详解——应用层说明一

文章目录写在前面1. 应用层模型1.1 需确认的应用层服务1.2 无需确认的应用层服务2 BACnet报文的分段2.1 报文分段规则2.1.1 APDU数据流的分段规则2.1.2 APDU最大长度的确定2.1.3 可接受的最大分段数2.2 分段协议控制信息&#xff08;PCI&#xff09;写在前面 年关将至&#xf…

分享77个PHP源码,总有一款适合您

PHP源码 分享77个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 77个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/12hh-lhIVPL1bZw-d2sfVlQ?pwdvhqj 提取码&#xff…

selenium 操作已经打开的浏览器

有时通过selenium打开网站时&#xff0c;发现有些网站需要扫码登录&#xff0c;就很头疼&#xff0c;导致爬虫进展不下去。 如果继续想使用selenium进行数据抓取&#xff0c;下一步应该怎么办呢&#xff1f; 步骤一&#xff1a;创建文件夹 在电脑的D盘或者F盘或者合适的盘创建…

《Python数据分析基础教程:NumPy学习指南:第二版》读书笔记

内容 主要介绍了NumPy库中的函数。 组成方式 用非常零散的知识点串联成章节。 内容摘要 极简地展示了章节中所运用的函数。 第一章 arrange函数创建NumPy数组。 第二章 NumPy特性 在NumPy中&#xff0c;复数的虚部是用j表示的。如果数组中包含复数元素&#xff0c;则…

VMware vSphere 中 Clone 与 Template 的区别

VMware vSphere 中 Clone 与 Template 的区别 Clone&#xff08;克隆&#xff09;Template&#xff08;模板&#xff09;克隆在克隆过程中为正在运行的虚拟机创建一个精确的副本模板作为具有根据组织标准预先定义的配置的虚拟机的基线映像。克隆虚拟机使用相同的配置和安装的软…

【C语言初阶】指针

文章目录1.指针是什么2.指针和指针类型2.1指针的解引用2.1指针类型的意义3.野指针3.1野指针成因3.2如何规避野指针4.指针运算4.1指针-整数4.2指针-指针4.3指针的关系运算5.指针和数组6.二级指针7.指针数组1.指针是什么 指针理解的2个要点&#xff1a; 指针是内存中一个最小单元…

【GD32F427开发板试用】+使用USBFS轻松实现HID键盘应用

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;不锈钢铁侠 前言 最近有项目需要用到键盘自动输入功能&#xff0c;提升工作效率。故使用该开发板实现自定义输入内容并通过按键控制自动通过u…

STM32——外部中断

目录 外部中断简述 什么是外部中断 传统单片机与新型单片机外部中断区别 STM32外部中断请求 STM32中断线与IO口的对应 STM32 中断服务函数 外部中断与中断服务函数的对应 中断服务函数列表 STM32外部中断程序编写 常用的库函数 外部中断的一般配置步骤 外部中断简…

【ArcGIS微课1000例】0058:波段合成(CompositeBands)工具的使用

波段合成工具常见于遥感软件,例如Envi和Erdas等,用于将多个单波段数据合成为一个多波段数据集,在ArcGIS中也提供了波段合成的工具,使用灵活方便。 文章目录 一、波段合成工具介绍二、波段合成工具案例1. 输出Esri Grid格式2. 输出tif格式3. 输出jgp格式4. 输出其它格式一、…

sqlite 使用distinct时组合索引可能不是你想的那样

目录先来唠唠嗑吧~那一探究竟吧&#xff01;表结构及索引信息我的查询场景到底命中什么索引了&#xff1f;简单小结下~先来唠唠嗑吧~ 在使用sqlite作为词条数据库查询数据&#xff0c;发现有的sql执行很快&#xff0c;有的sql执行很慢&#xff0c;你以为是没有走索引吗&#x…

深入浅出进程控制

文章目录进程控制浅谈fork写时拷贝fork调用失败的原因进程终止进程退出的场景进程常见退出方法查看进程退出码echo $? :查看进程退出码exit和_exit进程等待进程等待的方法waitwaitpid获取子进程status宏定义查看进程是否正常退出&#xff0c;查看退出码再谈僵尸进程浅谈阻塞等…

基于.Net Core开发的支付SDK,简化支付功能开发

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 在我们做项目中&#xff0c;不管是电商系统、外卖系统、还是上门维修系统等等&#xff0c;都需要支付功能&#xff0c;这就需要我们与第三方支付平台进行对接&#xff0c;但是第三方平台文档&#xff0c;往往都存…

05语法分析——自下而上分析

文章目录一、自下而上分析基本问题二、算符优先分析构造FIRSTVT(P)的算法构造LASTVT(P)的算法构造优先表的算法三、LR分析法1.LR(0)构造LR(0)项目集规范族构造识别活前缀的DFA构造LR(0)分析表2.SLRSLR解决冲突办法SLR(1)分析表的构造算法3.LR(1)【规范LR】LR(1)项目集I的闭包状…