批量 材质 调整_游戏图形批量渲染及优化:Unity静态合批技术

作者:枸杞忧天

(本文首发于公众号“偶尔学学Unity”,文章仅为作者观点,不代表GWB立场)

最近在准备公司的技术分享,主题是入门批量渲染,想着反正也总结了,不如充几篇博客吧,也算显得没有那么半途而废,一举两得了。

所以,这次将分四篇介绍一下与批量渲染有关的知识:

第一篇的主题是静态合批;

第二篇的主题是动态合批;

第三篇的主题是实例化渲染;

第四篇的主题是优化骨骼蒙皮动画,以及两种常用的批量渲染方式:烘焙顶点动画与烘焙骨骼矩阵动画。

那就开始吧。

批量渲染

批量渲染其实是个老生常谈的话题,它的另一个名字叫做“合批”。在日常开发中,通常说到优化、提高帧率时,总是会提到它。

可以简单的理解为:批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义。

如果瓶颈在GPU,比如GPU性能偏差,或片段着色器过于复杂等,那么没准适当减少批处理,反而能达到优化的效果。

所以要做性能优化,还是应该先定位瓶颈到底在哪儿,然后再考虑优化方案,而不是一股脑的就啪啪啪合批。

当然,通常情况下,确实是以CPU出现瓶颈更为常见,所以适当的了解些批量渲染的技法,是有那么一丢丢必要的。

静态合批

静态合批是一种听起来很常用,但在大多数手游项目里又没那么常用的合批技术。

这里,我简单的将静态合批分为预处理阶段的合并,和运行阶段的批处理。

合并阶段

合并时,引擎将符合合批条件的渲染器身上的网格取出,对网格上的顶点进行空间变换,变换到合并根节点的坐标系下后,再合并成一个新的网格;这里需要注意的是,新网格是以若干个子网格的形式组合而成的,因为需要记录每一个合并前网格的索引数量和起始索引(相对于合并后的新网格)。

空间变换的目的,是为了“固化”顶点缓冲区和索引缓冲区内的数据,使其顶点位置等信息都在相同的坐标系下。这样运行时如果需要对合并后的对象进行空间变换(手动静态合批对象的根节点可被空间变换),则无需修改缓冲区内的顶点属性,只提供根节点的变换矩阵即可。

2c08f474859a06208bb63a9f42b3328c.png

尽管网格不同、材质不同,Unity也会将它们的网格进行合并

在Unity中,可以通过勾选静态批处理标记,让引擎在打包时自动合并;当然,也可以在运行时调用合并函数,手动合并。

d3168e12b0c813f6202394b98c1825e8.png

通过勾选开关标记单位参与静态合批

打包时的自动合并会膨胀场景文件,会在一定程度上影响场景的加载时间。

5e376c92949b2106c69af92ce2d7f1ee.png

包含静态合批的场景体积大了一丢丢

此外,不同平台对于合并是有顶点和索引数量限制的,超过此限制则会合并成多个新网格。

批处理阶段

运行时是否可以合批(Batch)成功,还取决于渲染器材质的设置。

290c9ea93b335e2d010f89458449ce78.png

使用不同的材质,会分为不同的批次

当然,如果手动替换过场景中所有Material,也会打断批次。

25e84294d83dd893c1476d3424b544af.png

运行时手动替换所有渲染器的材质球

2d76dcde912c8d42fe4d2fea902f8714.png

不再有合批发生

如果偷偷修改了渲染器使用的网格(不再使用合批后的大网格),也会打断批处理。

4e51b63ab73bcec3445e39552d1a5fbf.png

替换网格后,也不会再被批处理

除上述之外,还有一些不常用,且不太有用的知识点。

运行时动态加载?

动态加载并实例化一个带静态标记的GameObject到场景中,是不会被当做静态合批处理的。换言之,静态标记只被作为是打包时的考虑参数,并不会在运行时被引擎处理。如果在场景加载后,希望对手动实例化的单位进行静态合批,可以使用手动静态合批。

合并根节点的空间变换

自动静态合批的根节点在场景上,因此无法对其进行空间变换;而手动静态合批,因为根节点不是场景而是一个游戏对象,所以可以通过修改根节点的空间属性(位置、大小及缩放值),达到诸如移动整个合批单位的目的。

当然,即使修改了合批后对象的空间属性,顶点和索引缓冲区里的数据也不会被修改,引擎会在渲染前,通过ConstBuffer(UniformBuffer)传入根节点的变换矩阵,达到了整体变换的目的。

Batch ≠ DrawCall

一次静态合批,并不表示一定只有一次DrawCall命令的调用。

合并发生后,每个参与合批的网格信息(顶点、索引等)就会被最终确定,不再被修改。当一个参与合并的单位不显示时,如被设置为隐藏或被视椎体剔除,引擎并不会修改顶点缓冲区和索引缓冲区的内容,而会拆分若干个小的DrawCall来分次渲染。通过调整每个DrawCall的索引(起始索引、索引个数)来跳过不应该被显示的单位。

92a4150879ffc84c762222f3367f4387.png

通过两次DrawCall来跳过隐藏的三角形

由于,这些DrallCall之间几乎没有渲染状态的切换,效率较高,所以引擎也将其统计为一次合批(尽管包含若干个DrawCall)。

07ecdbaf726c7f64f83429bb4ac049c8.png

隐藏掉部分立方体后,形成了两次DrawCall

745de3be3cb557703ef7fd4dd1b8f39f.png

引擎的Statistics窗口、FrameDebugger和Profiler(Renderer)会体现出这种差异

静态合批包含了多个材质时,引擎在材质分组后的处理方式也是相同的,只是不同材质在渲染时,都使用了相同的顶点、索引缓冲区。

与直接使用大网格的不同

静态合批与直接使用大网格(是指直接制作而成,非静态合并产生的网格)的不同,主要体现在两方面。

其一,静态合批可以主动隐藏部分对象。静态合批在运行时,由于每个参与合并的对象可以通过起始索引等彼此区分,因此可以通过上述多次DrawCall的策略,实现隐藏指定的对象;而直接使用大网格,则无法做到这一点。

其二,静态合批可以有效参与CPU的视锥剔除。当有剔除发生时,被送进渲染管线的顶点数量就会减少(通过参数控制),也就意味着被顶点着色器处理的顶点会减少,提升了GPU的效率;而使用大网格渲染时,由于整个网格都会被送进渲染管线,因此每一个顶点都需要被顶点着色器处理,如果摄像机只能照到一点点,那么绝大多数参与计算的顶点最后都会被裁减掉,有一些浪费。

e721e68328d1bdea6f6f821dba69ed9f.png

大网格不会被视锥体剔除,全部顶点都会被送进渲染管线

c2543ad0584b38870156468db0bf38c5.png

静态合批会被视锥体剔除,只有部分顶点被送进渲染管线

0d31e6a170930966d41e1143b1e3a6ee.png

静态合批下合并后的网格

6770482bd07b0c8ea271bfd4d468e570.png

静态合批下,顶点着色器只处理了部分顶点

当然,这并不意味着静态合批一定就比使用大网格要更好。如果子网格数量非常多,视锥剔除时CPU的压力也会增加,所以具体情况具体分析吧~

静态合批的利弊

静态合批采用了以空间换时间的策略来提升渲染效率。

其优势在于:网格通常在预处理阶段(打包)时合并,运行时顶点、索引信息也不会发生变化,所以无需CPU消耗算力维护;若采用相同的材质,则以一次渲染命令,便可以同时渲染出多个本来相对独立的物体,减少了DrawCall的次数。

在渲染前,可以先进行视锥体剔除,减少了顶点着色器对不可见顶点的处理次数,提高了GPU的效率。

其弊端在于:合批后的网格会常驻内存,在有些场景下可能并不适用。比如森林中的每一棵树的网格都相同,如果对它采用静态合批策略,合批后的网格基本等同于:单颗树网格 x 树的数量,这对内存的消耗可能就十分巨大了。

310ce5ccbf19e26a9fa9c2929432bd0a.png

极端情况下可能导致内存占用过高

总而言之,静态合批在解决场景中材质基本相同、网格不同、且自始至终都保持静止的物体上时,很适用。

不出意外的话,下次更新的内容应该是动态合批。

下回见。

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

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

相关文章

【算法设计与分析】16 分治策略:快速排序(快速排序的时间复杂度计算)

上一篇文章学习了:【算法设计与分析】15 分治策略:芯片测试 文章目录1. 快速排序的基本思想1.2 时间复杂度的计算1.21 最坏情况时间复杂度计算1.22 最好情况时间复杂度1.23 平均时间复杂度计算2 总结1. 快速排序的基本思想 用首元素 x 作划分标准&#…

的run窗口不显示_「玩转deepin」如何安装VirtualBox增强功能使得deepin全屏显示?...

在Windows上通过Virtualbox安装deepin深度操作系统,安装完成后,为了让deepin可以在virtualbox中全屏显示,需要安装增强工具,但是不少同学点击安装增强工具后会出现无法安装的情况。在【设备】中点击【安装增强功能】时&#xff0c…

掘金浏览器插件安装图文教程

本文介绍如何安装掘金chrome插件: Chrome插件推荐 2018-02-27 21:53 chrome插件网近日推荐一款设计师开发人员人手必备的一款插件:掘金插件。有网友留言给笔者说希望整理出一套掘金插件详细的安装步骤。于是就有了今天的图文详解掘金插件安装步骤。第一…

【mysql技术内幕1】mysql基础架构-一条SQL查询语句是如何执行的

文章目录1 一条SQL查询语句是如何执行的2 mysql体系结构3 InnoDB存储引擎4 总结1 一条SQL查询语句是如何执行的 ​ 通常我们使用数据库,都是将数据库看成一个整体,我们的应用与数据库完全就是通过SQL语句进行交互。大多数开发者很少去了解数据库的内部实…

session实现机制_如何理解php session运行机制

php session运行机制就是客户端将session id传入到服务器中,服务器再根据session id找到对应的文件并将其反序列化得到session值,然后保存的时候先序列化再写入今天将要分享的知识点是PHP中的会话运行机制,对于熟悉PHP的开发者来说并不陌生&a…

【MySQL原理解析】01. 一条SQL查询语句是如何执行的

这是【MySQL原理解析】的第一篇文章,MySQL我看了很多的书与教程,对其原理有一定的理解,一直想写一系列的文章来把MySQL的原理给讲清楚,一直没有时间写,今天算是个开头吧。万事开头难,咱们先破了这个开头&am…

【转载】MOS开关(verilog)

MOS开关 一. MOS开关 1. NMOS 源极(d)接Gnd 一般情况下,可认为晶体管受栅极(g)电平VG的控制(control). 1). VGH,源极(s)与漏极(d)接通; 2). VGL,源极(s)与漏极(d)断开. 源极(s)与漏极(d)接通, 则漏极(d)被下拉到Gnd. 2. PMOS 源极(d)接VDD 1)…

【Linux进程、线程、任务调度】二 fork/vfork与写时拷贝 线程的本质 托孤 进程睡眠和等待队列

学习交流加(可免费帮忙下载CSDN资源):个人微信: liu1126137994学习交流资源分享qq群1(已满): 962535112学习交流资源分享qq群2(已满): 780902027学习交流资源…

用Log4Net来记录系统的日志信息

[http://www.cnblogs.com/xhwy/archive/2011/11/25/2263495.html] 几乎所有的大型应用都会有自己的用于跟踪调试的API。因为一旦程序被部署以后,就不太可能再利用专门的调试工具了。然而一个管理员可能需要有一套强大的日志系统来诊断和修复配置上的问题。 经验表明…

扩展插件_Adobe扩展工具插件系列

Adobe系列软件相信大家都已了解到其功能的强大,除了软件之外Adobe系列的插件的功能也是不可忽略的。今天给大家分享的几款Adobe系列超强PS扩展工具,都是一些摄影师、设计师经常用的到的,可以提升不少工作效率,大家千万别错过哦&am…

【Linux进程、线程、任务调度】三 CPU/IO消耗型进程 吞吐率/响应 SCHED_FIFO算法与SCHED_RR算法 SCHED_NORMAL算法和CFS算法 nice与renic chrt

学习交流加(可免费帮忙下载CSDN资源):个人微信: liu1126137994学习交流资源分享qq群1(已满): 962535112学习交流资源分享qq群2(已满): 780902027学习交流资源…

Java学习之路整理-技术书从入门到进阶最全50+本(珍藏版 )

学习交流加 个人微信:LyyCoder学习交流资源分享qq群1(已满): 962535112学习交流资源分享qq群2: 780902027一.速读一遍(最好在1~2天内完成) 人的大脑记忆力有限,在一天内快速看完一本…

问题 seata_架构设计 | 基于Seata中间件,微服务模式下事务管理

一、Seata简介1、Seata组件Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA、XA事务模式,为用户打造一站式的分布式解决方案。2、支持模式AT 模式基于支持本地 ACID 事务的关系型数…

程序员史诗级必读书单吐血整理四个维度系列80+本书(珍藏版)

互联网行业的特点是变化。若要提高互联网开发的技能,就必须跟上技术发展的步伐。埋首醉心于项目开发与实战,固然能够锤炼自己的开发技巧,却难免受限于经验与学识。 世界上并不存在速成的终南捷径,但阅读好的技术书籍,尤…

Quartus 中快速分配器件管脚

在quartus中分配器件管脚最笨的方法是对于器件手册一个一个的敲进去,这样做如果用到的管脚很好还没有发觉什么不好,但是当用到的器件管脚很多的时候就会发现很麻烦,而且容易出错。接下来我来介绍一种很方便的方法。 首先在txt文档中建立管脚和…

Web前端书单从HTML到JS到AJAX到HTTP从框架到全栈

前言:技术书阅读方法论 一.速读一遍(最好在1~2天内完成) 人的大脑记忆力有限,在一天内快速看完一本书会在大脑里留下深刻印象,对于之后复习以及总结都会有特别好的作用。 对于每一章的知识,先阅读标题&…

自定义背景_新版快绘精选:自定义背景墙 | 吊顶 | 云渲染滤镜升级

New自定义背景墙没有合适的背景墙样板?你需要一个全世界独一无二,独属于你monent的背景墙?没问题!无论是要这样↓还是这样↓快绘都能帮到你!-- 请观看视频 --New自定义吊顶除了可以自定义背景墙,新版快绘也…

cdn加载vue很慢_Vue.js 项目打包优化实践

首先上结果:把常用的 Vue,router,vuex,axios 的 runtime 包拆分了出来,改为 cdn;另外就是对于自己编写的业务代码进行分包,根据路由进行懒加载,可以较好的提高首屏加载速度。添加了全…

SharePoint 2010 网站模板要求在网站集中激活功能

从别人那里将其一个站点另存为模板,拷贝回来,上传到自己环境中的解决方案库里并激活。 然后新建站点,选择该模板,报如下错误: 解决方案: 打开在首要网站,网站操作---网站设置--网站集管理---网站…