热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Unity3d遮挡剔除(OcclusionCulling)优化【2019】

遮挡剔除的概念  很多童鞋的Unity作品完成后,发现场景卡的要死,尽管在模型阶段采用了很多优化建模方法,但还是卡顿。电脑端都这么卡&#x

遮挡剔除的概念

  很多童鞋的Unity作品完成后,发现场景卡的要死,尽管在模型阶段采用了很多优化建模方法,但还是卡顿。电脑端都这么卡,发布到移动端就更不用说了。遇到这种情况,很多童鞋急得团团转,不知如何是好。此时,就必须对场景进行深度优化,当然了,Unity深度优化的方法很多,本文小姐姐将和大家一起探讨Unity3d最重要也是最有效的场景优化方法-遮挡剔除(Occlusion culling),希望对遇到场景卡顿的童鞋有所帮助。当然,对于机器配置高的童鞋,虽然暂时没遇到卡顿,可别忘了,你的作品最终不是在你自己的机器上运行,而是要在配置不高的用户机或移动端运行的,所以,场景的深度优化是每个人绕不过的一道坎。

  那么什么是“遮挡剔除Occlusion culling”呢?通俗地讲,就是将距离相机当前位置最近的一组非透明物体完全遮挡(看不到)的物体,动态地禁用渲染(禁用渲染一些物体,游戏视图的渲染负担就会减轻,作品跑起来就会流畅一些),这就是“遮挡剔除”。简单地说,就是把被遮挡住的物体剔除掉。都被挡住看不见了,还不剔掉,留着这些物体,除了因增加系统渲染负担而导致场景卡顿外,没有任何意义。这只是对遮挡剔除的文字描述,为了让童鞋们直观地理解,下面我们用图解诠释。

  在认识“遮挡剔除”之前,得先了解什么是“视锥剔除( Frustum Culling)”。大家都知道,摄相机视野的空间形态是一个四棱锥,Unity称之为“视锥(Frustum)”,默认情况下,Unity会和人的眼睛一样,自动将视锥以外的物体动态地禁止渲染,也就是只渲染视锥以内的物体,这就是“视锥剔除”。一般情况下我们是看不到“视锥剔除”现象的,但“视锥剔除”在运行时会自动启用。

  下图左上角几条线封围的部分就是相机的视锥(俯视),因为未使用遮挡剔除,所以场景中的所有物体均可见。



在这里插入图片描述


  
下图为添加遮挡剔除并使用其可视化预览功能的情况。在右下角弹出的可视化预览面板中,激活“Camera Volumes(相机体积)”,取消“Occlusion Culling(遮挡剔除)”复选框的勾选,目的是先禁用遮挡剔除,只预览“视锥剔除”效果。说到这里,有些童鞋肯定有些晕,不是说添加了遮挡剔除,怎么有禁用了呢?因为小姐姐是想先给大家直观地展示一下视锥剔除的效果,可一般情况下大家是看不到的,而遮挡剔除使用后,视锥剔除和遮挡剔除的效果都可以用可视化预览,并且取消预览面板中的“Occlusion Culling”复选,所预览到的就只是视锥剔除的效果了,这下明白了吧?由下图可见,视锥剔除后,只有视锥以内的物体可见,视锥以外的物体都被剔除掉了。对比上一张图和下图右下角游戏视图的预览,我们发现视锥剔除后,游戏视图中所看到的内容没有任何变化,这也就是说,视锥剔除不会改变游戏视图的视觉内容,但是由于剔除了视锥以外的内容,游戏视图相机的渲染量被大幅降低,如果不剔除,运行时游戏视图的相机会因渲染负担沉重而卡顿。视锥剔除是Unity3d的特性,无需特别设定,运行时会自动启用。说到这里,一定有童鞋说,既然这是Unity3d特性,那你费什么话,啰里啰嗦说半天视锥剔除?因为搞清楚了视锥剔除的原理,对于理解遮挡剔除是非常有帮助的。

在这里插入图片描述


  
好了,接下来我们就来讨论遮挡剔除。前面我们取消了预览面板中的“Occlusion Culling”复选,现在我们勾选这一项(如下图所示),也就是在Scene视图中除了预览视锥剔除的效果,也预览遮挡剔除的效果。勾选后,由下图可见,刚才视锥内保留的好多物体又消失了,这是因为相机处于室内,室内墙不透明,视锥内的室外物体都被室内墙挡住看不见,使用遮挡剔除后,那些物体也被剔除掉了,所以不可见了,但是对比下图右下角游戏视图的内容和前面没有任何剔除以及视锥剔除后游戏视图的内容,完全一样,也就是说,遮挡剔除后,也不会改变游戏视图的内容,但是在视锥剔除的基础上,又进一步大幅度降低了相机的渲染量,运行时作品会更加流畅,因为被剔除物体的几何、材质、光照贴图、粒子系统等都不再被渲染了。

在这里插入图片描述


  
通过上面的静帧图解,大家对视锥剔除和遮挡剔除有了形象的理解,但是运行时,相机的位置和方位角是动态变化的,所以被剔除和被保留的物体也是动态变化的。前面为了简化问题,小姐姐用了俯视视角图解了视锥剔除和遮挡剔除,下面我们用动态GIF图像来看看实际遮挡剔除和视锥剔除,由于这里对图像大小有限制,所以GIF只能截取很短的片段,能说明问题就好。

  下图左侧是游戏视图,右侧是Scene视图,从Scene视图可见,应用遮挡剔除后,当相机移动时,很多物体被视锥和因遮挡被动态地剔除了,但是游戏视图始终是完整的,根本看不到哪些物体被剔除。这里说明一下,右侧视图当中地面深色的东西是地面的光照贴图,不是物体,剔除后保留的物体只有相机前方很少的一部分,场景的全貌如第一帧所示,大家可以仔细观察右侧视图中保留物体的动态变化。


在这里插入图片描述

  
由于上图右侧视图太小,下面我们拉近并手工拖动相机观察一下遮挡剔除和视锥剔除的综合效果(如下图所示)


在这里插入图片描述

  
到这里,相信大家对什么是遮挡剔除和视锥剔除有了直观的认识和理解,需要说明的是,如果将整个场景合并为一个物体,遮挡剔除和视锥剔除没有任何效果,为什么呢?因为视锥剔除是根据物体的最大外轮廓(或者说是物体的边界框)是否在视锥以内来确定是否被剔除,如果合并成一个物体,相机怎么移动,物体的边界框都在视锥以内。对于遮挡剔除来说,都合并成一个物体了,哪还有被遮挡的物体呢?所以要通过遮挡剔除和视锥剔除优化场景,严禁将所有物体合并为一个物体,除非场景规模非常小。需要说明的是,这只是剔除静态物体的例子,对于动态物体的遮挡剔除,后面再说。

  遮挡剔除的过程是使用一个虚拟相机构建潜在可见物体集合的层级结构数据,好让每个摄像机在运行时使用这个数据来识别哪些物体可见,哪些物体不可见。 有了这些数据,Unity的相机将只对可见物体渲染,不仅降低了绘制调用(draw calls)的次数,并可提高游戏的性能。

  遮挡剔除的数据由许多单元格(前面GIF图像中Scene视图中的蓝色3d线框)组成,每个单元格是整个场景边界体积的细分,具体地说就是这些单元格构成了一个二叉树结构。遮挡剔除使用了两个树结构,一个用于视图单元(静态物体),另一个用于目标单元(动态物体象)。 所有视图单元映射到定义可见静态物体的索引列表,从而为静态对象提供更准确的剔除效果。

  在建模时要记住很重要的一点,即要考虑在物体大小和遮挡剔除的单元格大小之间求得最佳平衡。理想情况下,不应该有比物体小太多的单元格,同样地,也不应该让物体的尺度比单元格大到跨越了许多单元格。可以考虑在建模软件中将大的物体分解成较小的多个物体来改善剔除,但有时也需要考虑将小的物体合并在一起以减少绘制调用(draw calls),并且只要它们都属于同一个单元格,就不会影响遮挡剔除。

  可以使用场景视图的“overdraw(绘制重叠)”模式(如下图所示),定性地来查看绘制重叠(之所以发生绘制重叠,是因为物体出现了相互遮挡)的情况,也就是查看场景中物体的相互遮挡情况。



在这里插入图片描述

  
我们还是以前面的场景为例,将Scene视图由Shader改为overdraw模式,如下图所示,这是使用未遮挡剔除的场景。overdraw模式用来查看场景中物体的遮挡情况,由下面中间图可见,矩形框所标注的区域亮度很高,说明这个区域物体重叠严重,也就是被遮挡的物体很多。另外,可通过游戏视图的状态信息面板,定量地查看游戏场景的状态,如下面右图右上角所示。从状态信息可见没有遮挡剔除时,批次数量(Batches)为296(这个值越高,越卡顿,2017版之前用Drawcall来表示),三角面约42万,顶点数约62万。

在这里插入图片描述

  
下图是使用遮挡剔除后的情况。由下面中间图可见,矩形框所标注的区域亮度明显降低,说明这个区域物体重叠已经消除,也就是被遮挡的物体已经被剔除。由下面右图可见,批次数量降至62,三角面降至约15万,顶点数降至约21万。这里说明一下,批次数量(batches)的值是游戏是否运行流畅的重要标志,场景优化的目标就是降低批次数量、三角形数量、顶点数量,这些值越低,作品运行越流畅,否则会很卡顿。

在这里插入图片描述
  由上面的图解再一次说明,遮挡剔除对于优化场景性能非常重要,也非常有效。

遮挡剔除对模型的要求

  要使用遮挡剔除,模型必须在建模软件中被分解成合理尺寸的大小,以适应unity中遮挡剔除的单元格细分尺寸。在建模软件中尽可能把模型分解成比较小的几何体,且被比较大的物体(例如墙壁,建筑物等)遮挡的布局对于unity中的遮挡剔除优化是很有帮助的。

遮挡剔除的一般设置

  在检视面板中,将要参与遮挡剔除计算的静态物体标记为Occluder Static(静态遮挡物)或Occludee Static(静态被遮挡物),如下图所示。Occluder是遮挡物,也就是遮挡其他物体的物体,Occludee是被遮挡物,也就是被其他物体遮挡的物体。有的物体需要同时勾选Occluder Static和Occludee Static,也就是这个物体既遮挡别的物体,也会被别的物体遮挡。有的物体只需勾选Occluder Static,例如比较大的物体,它是永远不会被其他物体遮挡。而有的物体只需勾选Occludee Static,例如比较小的物体,因为它遮挡其他物体的可能性很小,单独勾选Occluder Static或Occludee Static,可减少遮挡剔除的计算量,即可加快遮挡剔除的烘焙。如果将场景中比较大且不可能被其他物体遮挡的物体设置Occludee Static,或者将比较小且不可能遮挡别的物体的物体设为Occluder Static,除了浪费烘焙时间和增大遮挡剔除数据文件外,毫无意义,所以根据物体的实际情况,合理地对其进行遮挡剔除设置,对加快遮挡剔除烘焙和优化遮挡剔除数据非常重要。另外补充一点,很多人在烘焙场景时,按照官方的要求,将要烘焙的静态物体直接设置为“Static(静态)“,也就是将检视面板右上角的“Static”复选框直接勾选,可当展开“Static”后面的下拉菜单的三角符时,发现后面有一堆这静态那静态的选项,如果直接勾选Static,就等于把这个下拉菜单里的所有选项(除Noting外)都勾选了,小姐姐认为,在烘焙场景时,不要直接勾选“Static”,而只需勾选“Lightmap Static”,全部勾上会给烘焙增加多余的计算负担和时间,做什么计算,就勾什么选项,全部勾上,进行遮挡剔除计算时不分青红皂白,会浪费很多时间,原因前面已经说过了,这里再次提醒一下。

在这里插入图片描述
  这里特别说明一下,当使用LOD Group优化时,只有LOD0可作为遮挡物。

  设置物体的Occluder Static和Occludee Static,也可以在遮挡剔除面板中完成,即单击打开菜单Window > Rendering > Occlusion Culling,在弹出的面板(如下图所示)中选择“Object”,并且在场景中选择“网格渲染器(也就是场景中的物体)”,遮挡剔除面板下方即会出现Occluder Static和Occludee Static两个复选框,在这里勾选,和前面设置物体的Occluder Static和Occludee Static属性异曲同工。遮挡剔除面板各按钮的功能见下图。

在这里插入图片描述


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 解决文件名过长下载失败问题的jQuery方案
    本文介绍了使用jQuery解决文件名过长导致下载失败的问题。原方案中存在文件名部分丢失的问题,通过动态生成隐藏域表单并提交的方式来解决。详细的解决方案和代码示例在文章中给出。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 本文介绍了使用Rust语言编写、保存和编译程序的简单步骤。首先,打开记事本文件并编写程序代码,然后将代码保存到一个以.rs为扩展名的文件中。接下来,使用rustc命令来编译运行程序。最后,通过命令行运行编译后的程序,得到输出结果。如果遇到编译错误,可以下载Build Tools for Visual Studio 2017来解决。 ... [详细]
author-avatar
大市低开_127
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有