Unity基础:DrawCall从入门到精通
老生常谈的Draw Call,这是一篇很“完整”的文章,正文太长,随目录自取所需即可。正文:
Draw Call从来都不是问题。换句话说,一开始没有问题,直到你添加了一个又一个的渲染元素,最后渲染线程突然变成了新的瓶颈。你能猜出为什么现在阻止Unity Draw Call比以往任何时候都更加重要吗?
警告:这是一篇深入探讨的文章,让自己感到舒适并喝杯茶。如果你着急,可以根据目录来查找想看的内容。
初遇Draw Call可能有太多Draw Call的信号
弱信号:电池消耗过快弱信号:设备升温弱信号:游戏不能顺滑的运行弱信号:VR用户比以往任何时候都头晕强信号:Unity Profiler显示了渲染线程瓶颈
等一下...什么是Draw Call
Batches vs SetPasses计算Unity DrawCall
需要减少Draw Call的3个原因战斗:Unity Draw Call批处理
前提:合并Unity 材质(Materials)技术1:Unity静态批处理(Unity Static Batching)技术2:Unity GPU Instancing技术3:Unity动态批处理(Dynamic Batching)技术4:Unity运行时批处理API
实践才是检验真理的唯一标准
初遇Draw Call
就在几年前,我还是一个没有经验的年轻小伙子……尤其是在游戏编程方面。
我当时正在做我的第一个专业任务,任务很明确。
我只需要改进和实现现有游戏的几个游戏系统。
很公平。
这就是我几个月来一直专注的内容。为我的玩家创造乐趣。
问题是,游戏开发中的每个领域对我来说仍然还是个未知数。
并且我不停的问自己……
如果我不得不解决在我未涉及领域的问题时该怎么办?
这个简单的想法使我非常不安。毕竟,我不想让我的老板失望。他雇用我是有原因的,所以他当然希望我能完成我的任务。
当然我知道这种情况只有在我必须面对从未解决过的问题的时候才会发生。
而且我还没有准备好。
不管怎样,我很高兴不断为游戏添加内容,不再对此担心。一切进展顺利,我收到了很好的工作反馈。
更好的是,性能一直很出色……
但不幸的事发生了。
几个月后,我注意到有些事不对劲了。
我去了商店,开始注意到游戏有越来越多的负面评论。
虽然我已经习惯了一定比例的负面评论。这始终是将你的作品展示给全世界的一部分。
但是趋势使我担心。它比以往任何时候都更糟。
越来越多的用户开始抱怨电池耗电更快,设备发热比以往任何时候都高,并且游戏感觉太慢。
我花了一些时间将这些点连接起来。
我认为那一定是性能出现了问题。
我开始担心会损害到了用户体验。
更糟糕的是,我的客户会怎么看我?这肯定和我在游戏开发方面所做的工作有关。
忧虑迅速转变为压力。
虽然我习早已惯了压力。毕竟在几年前,我经常每天在大学里呆12个小时以上。
但是这次却有所不同。不再只是和我有关,我正在让某些用户感到失望。
怀着勇气,我开始深入挖掘未知的性能世界。
而且我特意在业余时间做这件事。
我很快了解了Unity Profiler。那个有价值的工具向我展示了渲染线程似乎花费了太长时间。但是我不知道为什么。
所以我一直在调查。
但是,无论我投入多少时间,问题的出现速度都比我解决问题的速度快。
我正在思考是否要放弃。
毕竟,也许游戏开发并不适合我。
但是后来,我成为了世界上最幸运的开发者之一。
我很幸运能看到一篇有关技术债务的出色在线文章。我意识到给我自己挖了坑。
但与此同时,我也受到启发。
在开发过程中,随着时间的推移,我引进了一些内容,这些内容使我在游戏中的Unity Draw Call数量激增。Draw Call是Cpu向GPU发起的一种在屏幕上绘制内容的请求。
130个Draw Call太多了吗?是的,一定是这样,我想。我添加了未优化的内容,这会导致电池消耗加速和游戏运行速度变慢,所以现在我需要着手对其进行优化。
因此,我开始了优化材质的漫长旅程。毕竟在Unity中,Draw Call与材质的设置高度相关。我为此花费了大量的时间。我造成了这个问题,所以应该由我来解决它。
不过,我并没有停止思考长期问题。
如果我遇到了这个问题,其他人也有可能遇到。
在我看来,解决这个问题的唯一方法就是创建流程来持续监控性能指标。
那是我接下去的工作。
在几天之内,我实现了一个原型系统来连续监视游戏中的Unity Draw Call数。我想确保将来人们(尤其是我自己)仅提交优化过的内容。
尽管如此,我依然仍努力遵守最后期限。
我知道我必须完成它。我确实完成了。
经过不懈的努力,我统一了绝大多数游戏中使用的材质,并大大减少了着色器的数量。
漫长的努力,我的抽Draw Call数量终于降低到60以下了。
新的工作流保证了每个人都遵从了性能指南,性能再次变得很棒。
我为此感到自豪。
但是,我仍然有内心的声音提醒我,我让这些玩家失望了。
他们曾经玩游戏很开心。在游戏中他们成为了朋友。他们希望通过游戏加强与家人的关系。
这就是为什么我努力减轻他们在评论中表现出来的痛苦的原因。
但是这些玩家再也没有回来。我永远失去了他们。
真是令人心碎。
通过失去这些玩家,使我了解了在整个项目过程中监控性能的重要性。
加载时间,帧速率,性能峰值,电池和电源效率……所有这些事情比我想象的要重要得多。
这是使我如此专注于游戏性能优化的决定性时刻之一。我学到了教训。
该游戏今天仍然表现良好。而且由于进行了优化,因此可以轻松的移植到性能较弱的平台。
从那时起,我几乎每天都在监控我的游戏性能。
但是我不是一个人做。
我有系统可以自动报告这些数字。当系统报告有问题的时候,我就会去调查。
随着虚拟现实获得了巨大的吸引力,监视Unity Draw Call的任务以往任何时候都更加重要。我们必须以72、90甚至144 Hz的帧速率进行渲染。这些时间不会给你可观的CPU预算。
可能有太多Draw Call的信号
在游戏开发过程中的任何特定时刻都需要注意一些重要的标志信息。
随着时间的流逝,您会发展出第六种感觉,每当你遇到这些情况时,都会在脊柱上流下一些寒气。
但是,症状只是症状。由于瓶颈可能来自多个角落,因此它们并不都是Draw Call的问题。为了使区别更加清楚,我将它们分为两类。
弱信号表示有比较微弱的可能是由高Draw Call产生。它们可以完全来自其他性能因素,例如OverDraw。
最后,强信号表示你的游戏很有可能已经使用了太多的Draw Call。
弱信号:电池消耗过快
手机电池通常在正常使用情况下可持续使用一到两天。
但是游戏擅长于从用户的锂离子电池中窃取能量。
但是,请注意,不同游戏的功耗会有所不同。经过优化的游戏将使您的CPU和GPU压力减少,从而减少功耗。
而且优化游戏非常重要,因为您的用户非常擅长注意到早餐玩游戏时消耗了多少电量。
即使你不关心用户(谁会不关心用户?),这仍然是与你相关的因素。
这就是为什么…
高效运行的游戏让你的玩家可以玩更长的时间。而且他们玩的越多,就可以出售了更多应用内购买,更多广告展示,获得更多的口碑,就会有更多的钱落在你的口袋(或你的老板的口袋)中,你说对不对?
优化电池消耗是一个很好的投资回报。
弱信号:设备升温
你从用户设备中获取的大部分能量都转换为热量(和光),惊不惊喜?
在冬天,这可能会很方便以使你的手变热。但是,仍然剩下3个季节,你的用户并不希望到带着充电宝到处跑。
我仍然记得我在柏林的时候在冬天命令Glühwein温暖我的手。我不喜欢以葡萄酒为基础的饮料,但我学会了欣赏自己手中的温暖感觉。
所以我一直买。
如果你的目标是一个虚拟现实设备,你应该特别优化能源效率。除非你正在开发一个应用程序来取代传统的面部晒黑。
弱信号:游戏不能顺滑的运行
想象一下……
你正在玩节奏慢的多人射击游戏。
但是,由于你是新手,所以每个人都比你玩的更好。
经过数小时的挫折之后,你终于发现了机会:一个分心的狙击手。
你在从后面偷偷接近他。他们还没注意到你,但你知道你的机会仅仅存在于他们背对你的一小段时间内。所以你瞄准了头部准备来一次完美的爆头。
你慢慢的把鼠标向上移动了几像素。
但是,经过半秒钟的延迟,你的十字准线现在突然指向天空。
狙击手注意到了你,等到你意识到的时候,你已经死了。
到底发生了什么事?
低帧率。
现在,由于多种原因可能会导致性能下降,但是,大量的Unity Draw Call无疑是其中之一。
我记得“反恐精英”的早期,人们使用最强大的GPU扔烟雾弹以获得不公平的优势。玩家如果使用低端计算机,计算机将无法真正很好地处理烟雾,因此他们最终死于低帧率。这可能更多是由于OverDraw而不是Draw Call,但我仍然值得一说。
弱信号:VR用户比以往任何时候都头晕
VR游戏的性能越低,用户的感受就会越差。
等等,我可以用不同的方式讲。
你潜在的Draw Call越多,你给用户糟糕的情况就越严重。
是的,这种说法更好。
强信号:Unity Profiler显示了渲染线程瓶颈
如果渲染线程执行所需的时间太长,则可能有太多的绘制调用。或更糟糕的是,您可能有太多的Set Passes Call(我们将在下一部分中看到不同之处)。
当渲染线程完成处理所有绘制调用之前,你的主线程可能闲着。
您可以通过Unity Profiler识别这种情况,如下所示。
Unity Draw Call:昂贵的渲染线程
你拥有的这些迹象越多,你的游戏中出现太多Draw Call的可能性就越高
我们已经谈论Draw Call很长时间了。
但是究竟是什么Draw Call?
等一下...什么是Draw Call?
简而言之,Draw Call 就是你的CPU要求你的GPU绘制一些东西。
就是你的CPU在说:嘿,用这些纹理和照明信息在那角落画这张椅子。
如果你有很多“东西”,那么你可能需要准备许多Draw Call:
我想要那个角落画椅子还有另一个角落画椅子哦,再画上书柜就更好了。
问题是,准备Unity Draw Call会占用CPU大量的时间和精力。Unity必须将场景内容转换为GPU可以理解的格式。这个过程开销最昂贵的部分是设置正确的渲染参数,例如纹理,着色器,网格等。
手动设置渲染参数很繁琐。这就是游戏开发人员引入材质概念的原因。
材质是一种包含有关如何绘制对象的信息的数据结构。它包含一个着色器及其所有参数,以及有关如何设置GPU渲染状态的信息。
而且,添加到场景中的每种材质都会增加渲染管线的复杂性。每种材质至少使用一个SetPass(用于设置渲染参数)。如果你希望自己的游戏表现出色,那么你真的需要将这些最小化。
这是否意味着我们不能一次绘制太多对象?
不是的。
游戏开发人员使用批处理将相似对象的渲染分组到同一个Draw Call中。这样,CPU只需支付一次绘制调用即可渲染多个对象。
在使用批处理时,我们要求GPU一次在这里,那里和后面画三把椅子,而不是在三个不同的时间。
现在,关键要理解什么是“相似”(对象)。
在批处理中,相似定义为在不同的对象上使用相同的材料。如果完成此步骤,则可以完成最复杂的步骤。
在Unity Frame Debugger的帮助下,你可以在下面看到4个Draw Call的顺序:3个用于家具,1个用于地板。
Unity Draw Call:无批处理
这样子开销真的很昂贵,但是分批处理将帮助我们减少这些Draw Call。这将减少播放器的CPU负载。拥有更多性能资源可以让你添加更多游戏功能,或者只是用这种方式减少设备的能耗。
Batches vs SetPasses
有一点细节,很少有开发人员知道。
在分析器和统计信息窗口中看到的“Batches”和“SetPasses”指标之间存在着差异。
这种差异具有巨大的影响。
Batches通常被我们称为绘图调用(Draw Call)。这些是简单的绘制命令,例如,在此处绘制此对象,然后在此处绘制另一个对象。这主要是关于使用当前全局渲染状态绘制相同着色器、相似参数的对象。
但是,SetPasses描述了一种更昂贵的操作:材质更改。更改材质很昂贵,因为我们必须设置一个新的渲染状态。其中包括着色器参数和管线设置,例如Alpha Blending,Z-Test,Z-Writing。
让我通过示例向你说明。
让我们考虑一下,有3个椅子使用相同的网格。
三把椅子
现在,我们将探讨三种具有不同批处理和材质设置的方案。每个方案将导致不同的Batches和SetPasses。请看下表。
方案:最坏情况方案:还是不行方案:好起来了方案:最佳批处理设置禁用启用禁用启用材质设置不同的(x3)不同的(x3)相同的(x1)相同的(x1)绘制事件1. SetPass(椅子1)
2 . Draw Call(椅子1)
3 . SetPass(椅子2)
4 . Draw Call(椅子2)
5 . SetPass(椅子3)
6 . Draw Call(椅子3)1. SetPass(椅子1)
2 . Draw Call(椅子1)
3 . SetPass(椅子2)
4 . Draw Call(椅子2)
5 . SetPass(椅子3)
6 . Draw Call(椅子3)1. SetPass(椅子材质)
2. Draw Call(椅子1)
3 . Draw Call(椅子2)
4. Draw Call(椅子3)1. SetPass(椅子材质)
2 。Draw Call(椅子1 + 2 + 3)SetPasses3个31个1个Batches (D.C.)3个3个3个1个性能最差最差好最好第一种情况和第二种情况相似:不同的材质使我们的SetPass数量猛增。在渲染线程中性能最差,无法进行批处理,因为批处理需要使用相同的材质。
但是,在第三种情况下,我们看到了一丝曙光。共享材质使一切变得不同。具有独特的材料可以将SetPass计数减少到1,这可以极大地提高性能。当然,我们仍然有3个Draw Call,但这些操作非常廉价。
最后,如果你真的想达到最优,那么第四种情况最适合你。在这里,我们启用批处理。批处理喜欢相同的材料。启用批处理可将Draw Call计数减少到1。在这里,我们得到了最理想的输出:1 SetPass,1 Batch
计算Unity DrawCall
在深入研究Draw Call之前,我们首先需要适当的工具来对其进行衡量。有很多可用的工具,例如RenderDoc,但我们将坚持使用最简单的方法:统计窗口和Unity Frame Debugger。
你可以随时通过单击游戏视图右上角的“统计”按钮来访问“统计”窗口。此面板向你显示当前游戏视图的指标。如果你的屏幕内容发生变化,则这些数字会不断变化(如果你认真对待游戏开发,肯定会有所变化)。
在那里,注意Batches(Draw Call)和SetPasses(材质变更)。如我们所见,它们是相互联系的,但是对性能的影响却并不相同。
Unity Performance Stats Window
最后,你可以并且应该使用Unity Frame Debugger。该工具将为你显示当前视图正在发出的特Draw Call(Batches)。你可以点击Window → Analysis → Frame Debugger菜单打开它。
需要减少Draw Call的3个原因
我的经验告诉我,即使SetPasses和Draw Call不是你的游戏瓶颈,但也至关重要的。这是我从项目开始就减少Draw Call的主要原因:
它使移植到未来平台变得更加容易。你肯定花了数千小时来开发游戏。这是每个开发者交付游戏都必须支付的基准成本。一旦你支付了该成本,为什么不从移植到其他平台中获利呢?你将花费一小部分成本使销量成倍增长。问题是,游戏的优化程度越高,移植过程中的工作量就越少。避免亡羊补牢。一开始就对其进行优化!你可能知道优化你的同事一年前创建的十几种资源的感觉,尤其是当你的同事离开项目时。这在Asset Store购物时尤其要注意的是:商店中的大多数资源并不完善,很可能使用多种材质设置。这将导致不可能以移动端作为兼容目标。它可以使你的游戏提高游戏效率。将使用更少的CPU资源,并获得更好的性能,这在VR中至关重要。你将从玩家的电池中窃取更少的能量。玩家和社区将通过更好的评论和买更多的应用内购来奖励你。用户玩游戏的时间越长,他们看到的广告越多,他们内容付费的速度就越快。
在使用Unity时,默认情况下你会不由自主的添加Draw Call。除非你有意识注意资产的性能,否则资产多数会更倾向于使用不同的材质。随着时间的流逝,不同的材质不断被添加,各种不能合批的Draw Call也会增加。这将会导致性能爆炸。
战斗:Unty Draw Call批处理
我们不希望绘制一个对象10次,而是一次绘制了10个对象。
这就是批处理(batching)的力量。
批处理Draw Call的主要要求是对象使用相同的绘制属性(材质)。在这种情况下,Unity可以将不同的网格合并为使用统一材质的单个网格。
就想前面说的,默认情况下,大多数资源将使用不同的材质。但是不用担心,我们将看到将几种材质合并为单个方法。
下面是一张流程图,总结了在Unity中进行合批的方法和限制(个人补充:静态批处理复杂场景不要用,可能会导致渲染队列混乱,产生严重的OverDraw;)
Unity Draw Call Reduction: The Guru’s Batching Diagram
你的切入点是要查找要那些对象需要使用相同的材质。
使用相同的材质是批处理工作的前提。不同的材质具有不同的绘图设置,这些设置会更改全局GPU渲染状态。
如果这些对象不使用完全相同的材料,但它们足够相似,则必须将它们合并为一个。这通常涉及创建共享的纹理图集,并更新单个对象的UV坐标以指向新的正确位置。下面会提到一些可以帮助你的工具。
一旦你的对象使用相同的材质,便可以选择许多方法来批处理这些Draw Call。
我建议您使用的批处理技术取决于要批处理的对象的性质。
但是在进行批处理之前,让我们看看如何合并材质。
填写邮箱下载图表PDF高清版本
全分辨率工作流程图图表的可打印版本还有Unity示例工程
现在下载
下一个…
前提:合并Unity 材质(Materials)
合并材质的第一个要求是:
要批处理的对象的必须使用相同的着色器材质
更换当前着色器(Shader)是你可以执行的最昂贵的操作之一。这会大大降低渲染速度。
几乎每个游戏都必须在某种程度上更换着色器,这很正常,现在你知道它的切换成本,就应该尝试减少项目中的着色器数量(包括着色器变体)。
如果你可以将两个相似的着色器合并到同一着色器中,那么你将获得巨大的性能优势。
因此,第一步是尽可能从项目中减少着色器。处理完成之后,你会得到许多原始材质,即使它们使用相同的Shader,看起来的效果和之前也没什么区别。
一旦目标对象使用相同的着色器,下一步就是合并其材质。这可能很复杂,因为他们可能具有不同的材质参数,例如:
纹理:每种材质通常具有一个或多个与其他材质不共享的纹理。在不同材质上使用相同纹理的一种方法是创建包含所有单独纹理的较大纹理。这些纹理称为图集。材质参数:例如金属,镜面反射和其他参数。要合并这些值,你可以找到适合所有条件的共同平均值,也可以在特定通道中创建包含该值的纹理图集。你可以将3个或4个纹理通道用于不同的参数,例如将金属值存储在红色通道中。
现在,你有了多个具有相同材质的对象,但它们必须具有不同的参数,则可以把他们收集成为MaterialPropertyBlock。你可以为每个需要自定义参数的渲染器创建MaterialPropertyBlock,而不是创建单个材质实例。然后,你可以在每个Block中设置自己的参数。虽然这不会减少Draw Call的次数,但是会降低渲染的成本,因为你明确地告诉了Unity每个对象的不同之处。
为共享着色器的材质创建纹理图集通常遵循以下几个步骤:
创建一个大纹理,我们将其称为纹理图集。获取所有材质的纹理通道,并将其纹理复制到新创建的纹理图集中。遍历使用这些材料的网格以重新计算其UV。新的UV将指向包含原始纹理的纹理图集的新的子区域。禁用旧的网格,然后使用具有更新的UV的新网格。使用合并过的材质替换原本材质。对着色器使用的每个纹理属性重复所有这些步骤。
我建议你在3d软件中执行此操作。如果有时间的话,这是最好的方法,因为它可以使你更好地控制过程。这可以提高输出质量,因为你可以调整关键变量,例如纹理像素分辨率。你还可以应用更高级的技术,例如调色板。
如果你没有时间、资源或经验去做这些事,那么我将带你走另一条路……
使用工具为你执行此操作。
作为程序员,我使用3D建模工具的效率不高,因此我经常使用这些方法。
以下是一些可用的Unity软件包,你可以使用它们在Unity中合并材质:
Mesh Baker:高级或免费Super CombinerAdvanced BatcherOne BatchMesh Combine Studio 2(个人推荐)
一旦我们将多种材质合并为一种材质,便可以开对Draw Call进行合批了。
你需要一些标准来帮助你在不同的批处理方法之间进行选择。
所有的批处理技术都是要付出代价的。他们都有一些限制,如何使用需要根据你的游戏情况来选择。
让我们按照顺序对其进行学习。
技术1:Unity静态批处理(Unity Static Batching)
静态批处理默认状态下处于启用状态(我建议保持这种方式)。
Unity自动将此技术应用于场景中共享材质的所有静态对象。我们来看以下示例:
绘制静态的椅子1绘制静态的椅子2绘制静态的桌子
如果使用静态批处理,就会把他们合并成一个Draw Call:
绘制静态餐椅(包含2把椅子和一张桌子)。
Unity Draw Call Batching 设置
你可以在Player Setting下找到这个设置,如图所示。选择您要启用的目标平台。
请注意,稍后在player settings中也会启用/禁用动态批处理(Dynamic Batching)。
更准确地说,Unity将查找启用了batching static标志的对象。然后,Unity将尝试合并公用材质的对象。
Unity静态批处理通过创建包含各个网格的巨大网格来工作。但是Unity也会保持原始网格的完整,因此我们仍然能够单独渲染它们。这样我们可以仅绘制可见视野内的对象,而丢弃不可见的对象,使得视锥裁切正常工作。
通过将所有网格都放在一个网格中,我们就可以在不更改渲染状态的情况下全部绘制它们。
静态批处理仅在你按下编辑器上的“Play”按钮之前发生,并且在构建时也会发生。Unity尝试遍历每个场景,并尝试批处理尽可能多的静态对象。
Unity Draw Calls: Static Batching
静态批处理的主要限制是每批可以具有的顶点和索引的数量,通常为每个64k,可以在此处检查限制更新(如果有)。
静态批处理的缺点是增加了内存使用量。如果您有100个石头,每个石头模型占用1MB,则可以预期内存使用量将超过100MB。发生这种情况的原因是,巨大的批处理网格将所有石头一起包含在一个网格中。
但是,内存使用对你来说不是问题。毕竟,你可以查看我的Unity可寻址对象教程,该教程将帮助你节省大量内存。
技术2:Unity GPU Instancing
GPU Instancing是我最喜欢的批处理技术之一,因为它可用于非静态对象。
如果我们有这些Draw Call:
绘制动态石头1…绘制动态石头100
然后使用GPU Instancing将它们转换为一个Draw Call:
在这里到那里画100个动态的石头…
这是针对每种材质激活GPU Instancing设置,如下所示。
Unity GPU Instancing:材质设置
GPU实例化让你可以非常高效地绘制相同的网格几次。Unity通过向GPU传递转一个Transform列表来做到这一点。毕竟,每块石头都有自己的位置,旋转和缩放。
与静态批处理相比,这是一项强大的技术,因为它不会激增内存使用量,并且不需要对象是静态的。
Unity Draw Calls: GPU Instancing
要使用GPU Instancing,你只需要在材质检视面板中启用它即可。如果你有多个具有相同网格和材质的对象,那么Unity对它们将自动进行批处理。
但是,创建Transform列表会降低性能。如果在游戏过程中没有物体移动/旋转/缩放,则只需支付一次此开销。但是,如果对象每帧都更改一次,则需要每帧支付一次开销。
推荐一个插件:GPUInstance比Unity默认的要好用的多。
技术3:Unity动态批处理(Dynamic Batching)
如果你不能满足静态批处理和GPU Instancing的使用条件,你仍然有希望。
你可以对使用不同网格物体的动态对象进行动态批处理。
Unity Draw Calls: Dynamic Batching
但是,请记住,Unity动态批处理受到更加严格的限制。你只能将其应用于具有少于300个顶点和900个顶点属性(颜色,UV等)的网格。材质也应使用single-pass着色器。此处有完整的限制列表。
出现此限制的原因是在运行时创建这些批处理的CPU性能成本。与单独发出绘图调用相比,超过300个顶点很难证明批量CPU的成本合理。
不仅如此,动态批处理非常不可预测。你无法真正确定对象将如何被批处理。结果通常会随着帧的变化而变化。打开Unity Frame Debugger并查看结果,在每帧之间动态批处理的结果发生巨大变化是令人困惑的。
我认为,这应该是你的不得已的方法。但是,嘿,它仍然是一种有用的工具,请不要忽略它。
技术4:Unity运行时批处理API
如果在特殊情况下,希望对批处理进行更好控制,可以用手动进行。
不用担心,你不必自己处理网格顶点。
Unity使你可以访问2个强大的API,以在运行时合并网格。
假设你正在开车。在车的内部,你会看到一些元素,例如座椅,把手,挡风玻璃和你收藏的咖啡杯。你可以在比赛开始之前设定这些元素。
一旦你做出选择并开始比赛,这些元素就会在你的赛车中变成静态的(无法再次更改)。让我解释一:
汽车本身是动态的。毕竟,你正在开车。
但是它所有不动的内部零件?可以认为它们相对于汽车对象是静态的。挡风玻璃将始终留在车内的同一位置。
但是,Unity认为所有这些都是动态的。这就是为什么在这种情况下无法进行静态批处理的原因。
尽管如此,我们仍然可以通过使用静态批处理API,手动创建合批。
最简单的方法是使用StaticBatchingUtility.Combine。该函数传入一个根游戏对象,并将遍历其所有子对象并将其几何形状合并为一个大块。一个容易遗漏的限制是,要批处理的所有子网格的导入设置必须允许开启CPU read/write。
第二种方法是使用Mesh.CombineMeshes。此函数间接获取网格列表并创建组合的网格。然后,你可以将该网格分配给mesh filter渲染。
我简化了这两种功能的解释。查看文档以获取有关如何使用它们的详细信息。
在下图中,您将看到我如何应用StaticBatchingUtility API的功能在运行时将一些动态坦克批合并为一个网格。
Unity Draw Call:使用StaticBatchingUtility.Combine进行手动批处理
实践才是检验真理的唯一标准
我知道…
有这么多的可能性,好处和局限性,Unity中的Draw Call批处理最初可能会令人不知所措。
很难在一个小时内理解我花了多少年才掌握的东西。
但是你可以采取捷径。
这就是为什么我准备了一个资源包来指导你优化游戏的原因。
这是您将得到的:
批处理解析图的全分辨率图像,包括可打印的版本带有四个场景的Unity项目向你展示了我如何应用四种批处理技术
填写邮箱立刻获取。
本文翻译由作者授权 收藏一波,我就学会了 原文发一下呗 收藏 太啰嗦了。。。 https://thegamedev.guru/unity-performance/draw-call-optimization/#see-draw-call-batching-in-action 我记得ecs框架里有SharedComponentData应该用的也是类似技术 不知道作者有没有了解过 你指的是哪一个技术? 就是处理共享材质 感觉本质上也是把相同的绑在一起调用 看别人用过而且效率也挺高的 但是我了解不多 yeah mark first
页:
[1]