| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 游戏开发 -> Unity的绘制调用批处理(Draw call batching) -> 正文阅读 |
|
[游戏开发]Unity的绘制调用批处理(Draw call batching) |
Unity版本Unity2020.3(LTS) 概述要在屏幕上绘制一个物体,引擎必须发送一个绘制命令(Draw Call)给图形的API,比如OpenGL或者Direct3D的接口。由于每次Draw Call,CPU要准备大量的数据,往往会引起较大的CPU的性能消耗。因此我们需要想办法减少Draw Call的数量。 一般情况下,Unity有两种比较方便使用的减少Draw Call的方法:动态批处理和静态批处理。 相比手动的去合并Mesh,Unity的合批有一个最大的优点就是仍然可以进行单个对象的剔除,当然也有缺点,比如静态批处理会增加内存的消耗并且需要额外的存储,动态批处理由于需要动态的合并Mesh,需要额外的CPU消耗。 我们可以通过Unity的设置开关批处理 批处理的材质设置(一些注意点)只有用相同材质球的物体才能够被合批处理,所以我们想要更好的批处理效果,就需要尽可能的让不同的物体使用相同的材质球。 如果我们的材质球之间的区别仅仅是需要使用不同的贴图,那么我们就可以将这些贴图合并到一张大贴图中来实现使用同一个材质球的需求。我们一般用的UI图集就是这么处理的,所以我们的UI有那么多的绘制对象却只需要较少Draw Call。 如果我们需要在代码里面修改材质,要注意不能用Renderer.material接口,这个会导致复制一份材质实例出来,要用Renderer.sharedMaterial,这样材质球才是被共用的。 阴影的绘制会比较特殊,即使材质不一样,也基本上能合批,只要在阴影的Pass中用到的材质中的参数值是一样的,比如物体用了不同的贴图,但由于阴影Pass中不需要使用贴图,所以它们的阴影还是可以合批的
动态合批(Dynamic batching)Unity可以对满足条件的相同材质的移动物体进行动态的合批,并且这个合批只需要开启即可,不需要我们做额外的工作。 动态合批的限制顶点数量限制由于要动态的将物体进行合并为一个Mesh,所以合并的物体的顶点数量肯定不能很多,否则合并的性能消耗太大,反而得不偿失 Scale不允许有负值其他条件满足后,Scale只要是正值,是多少都可以合批,但只要有任意的负值,这个物体就不再能被合并
必须是同一个材质实例如果是不同的材质实例,即使他们所有的参数都一模一样,也会导致无法动态合批,所以在修改材质时不能用Renderer.material,而是要用Renderer.sharedMaterial
不允许设置额外的光照贴图渲染参数设置了lightmap Index和Offset/Scale的无法进行动态合批,这个设置更多的是静态烘培后会有的设置,所以更适合用静态合批,如果设置了的话,动态合批就无法设置了
我们烘培后,拿几个物体设置为完全一样的贴图设置 多Pass的Shader会打断合批几乎所有的UnityShader都支持多个灯光的前向渲染模式(Forward Rendering),这就要求多个Pass,额外的像素灯光的DrawCall就无法合批了 在不同的平台上动态合批并不一定具有优势动态合批是通过将所有的物体顶点转换到世界坐标中,然后发送到GPU中,所以只有这个工作量小的时候相比绘制每一个物体才会有优势。一次绘制调用所要占用的资源受到很多因素影响,最主要的就是使用的图形API,比如对于游戏主机或者像Apple Metal的现代图形API,一次绘制调用的性能消耗比较小,这样一来动态合批就没有什么优势了。 其它渲染器的动态合批对于粒子系统(Particle Systems)、Line Renderers、Trail Renderers这些并不是由Mesh构成,而是Unity动态生成的带有几何图形的组件,它们的动态合批方式和Mesh的不一样 (说实话,是官方文档写的不好,还是我学的不好,哪怕有中文的文档了,也搞不太懂…) 静态批处理静态批处理可以合并任意大小的Mesh以减少绘制调用,不过得是使用相同的材质,并且不能移动。由于不需要在CPU上转换顶点,因此静态批处理比动态批处理效率更高,但是需要更多的内存 要静态合批的对象必须使用的是相同的顶点属性,比如顶点位置,顶点法线,和一套UV的对象可以合批,但是这种对象和顶点位置、顶点法线,两套UV、顶点切线的对象就无法合批了。(总觉得Unity的文档连表达都不清楚,上面说了得是相同的材质,为啥还要扯这些东西,难道还有相同材质使用不同顶点属性的?那上面还说相同材质,真实醉了) Unity不能静态合并具有镜像Transform的对象(这个有问题) 两个Cube相互大小镜像,但能静态合批 使用静态批处理需要存储合并的Mesh,如果几个物体在静态合批之前用的是相同的Mesh,但在合批之后每个物体的Mesh都相当于多了一份,因为会生成一份大的Mesh。所以使用静态批处理并不一定是最好的方法,有时候我们不得不不做静态合批,牺牲一些渲染性能让内存占用更小一些。比如在一些有茂密树的关卡中,如果这些树要静态批处理,就会有严重的内存占用。 静态批处理内部原理是通过将静态的物体转换到世界坐标并且为它们构建一个共同的顶点和索引缓冲,如果我们开启了PlayerSettings中的Optimized Mesh Data选项,Unity在构建顶点缓冲时会移除没有使用到的顶点属性(必须是所有的Shader变体都没有使用)。有一些特殊的关键字(Keyword)用来做这个检查,比如Unity如果没有检测到LIGHTMAP_ON关键字,就会在批处理中删除光照贴图UV。然后,对于同一个批次中的可见物体,Unity会执行一系列简单的绘制调用,这些绘制调用之间几乎没有状态的变化。技术上来说,Unity不会减少图形API的调用,但是减少了调用之间的状态变化,而这些状态变化正是消耗大的部分。静态合批有顶点和索引的数量限制,在大部分平台上是64k的顶点,64k的索引,在OpenGLES上索引限制是48k,在macOS上索引限制是32k。 在当前,仅仅Mesh Renderers,Trail Renderers,Line Renderers,Particle Systems和Sprite Renderers能够合批,也就是说,Skinned Meshes,Cloth和其它类型的渲染组件没办法合批。 对于透明物体,为了透明效果,这些物体是按从后往前的顺序进行渲染的,Unity首先会按这个顺序进行排序,然后尝试对它们进行合批,但是因为要保证正确的渲染顺序,所以能进行静态合批的透明物体比不透明物体会更少。 手动的合并比较靠近的物体是一个合并DrawCall比较好的替代方法,在功能允许的情况下,将能合并的物体都手动合并到一个Mesh中,比如一个静态的柜子,有许多抽屉,这些抽屉就能和柜子一起合并为一个Mesh。我们可以通过其它的3D软件或者通过Mesh.CombineMeshes来实现手动合并。 我的总结Unity的这个合批说明只能了解个大概,大概知道有哪些限制,大体是怎么使用,但Unity文档自己都没讲清楚,要嘛偷懒,要嘛就是写文档的人自己都不清楚,所以真要想优化合批,还是得自己在项目里实践,最主要的工具就是通过FrameDebuger分析,里面会有具体的不能合批理由,这个非常实用 本文https://blog.csdn.net/ithot/article/details/122262314?spm=1001.2014.3001.5502 本文测试工程https://github.com/MonkeyTomato/DrawCallBatchingTest 参考本文主要根据Unity的官方文档整理而来 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/27 18:46:48- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |