Unity3D高级编程3D模型与动画合并3D模型技术
Animation 和 Animator的选择。===
首先说明Unity3D引擎已经不再对Animation动画系统进行维护。但不维护也并不是说一定不能用,很多旧的项目任然在用,只是在老版本中使用。新动画系统 Mecanim 中有了新的动画组件 Animator,为什么要用新系统 Mecanim 呢?
原因如下几个方面:
Mecanim 系统使用多线程计算,比Animation的单线程性能要高出一点。
Unity3D本身就自带对 Mecanim 系统的优化选项“Optimize GameObject”。开启该选项,Animator.Update和MeshSkinning.Update的CPU占用均会一定程度的降低。
Animator的功能更加多,Retargeting功能让不同角色使用同一套动画资源,比如游戏中的角色的空闲动画,就可以使用同一个动画文件省去了动画资源内存的开销。Animator状态机的接入,让动画可以在不同的条件下可以自动的切换。
Animation因为Unity3D引擎不再维护了,大多数人选择Animator这是正确的做法。其实就我而言我更喜欢Animation,但现实是也不得不抛弃它去投奔新‘主子’(Animator),因为不再维护意味着从长远来看它会越来越糟糕。
Unity3D 3D模型中SubMesh的意义
在模型中可以有很多网格,一个模型可以由很多个网格构成。因此在Unity3D中一个Mesh网格的构成可以由多个子Mesh组成也就是SubMesh,即一个Mesh里可以有多个SubMesh。
引擎在渲染的时候,每个SubMesh都需要对应一个Material材质球来匹配做渲染,说白了一个SubMesh本身就是普通的模型有很多个三角形构成它也需要材质球支持以达成渲染。在美术人员制作3D模型过程中,可以将SubMesh拆分成独立的Mesh,也可以并成多个子模型即SubMesh。
这里可能大家有个疑问,为什么美术人员在制作3D模型时不把网格都编成一个而要制作成多个SubMesh?这是有原因:
一种情况是,3D模型制作人员在制作模型的时候,希望一个模型中一部分Mesh用一种材质球来表现效果,另一部分Mesh则用另一种材质球来表现效果,这时就需要将模型拆分开来。因为一个Mesh只能对应一个材质球做渲染,一个材质球只能表现一种效果,当他们需要表现两种完全不同的效果时就需要拆分。
第二种情况是,模型中的某部分的贴图,在众多模型**同使用的频率比较高,为了不重复制作以及减少重复劳动,那么就会让原本可以整体的模型单独拆分出来一部分公共材质的部分让它们都使用同一个材质球。
第三种情况是,在制作动画时,由于动画过于复杂导致如果使用同一个模型去表现的话,骨骼数量就会成倍增加。为了能更好的表现动画,也为了能更节省骨骼的使用量,拆分出一部分模型让他们单独成为模型动画的一部分。
以上三种情况都是我们在制作模型过程中需要着重考虑的问题,通常情况下都会用拆分模型的方式来解决这些问题。
其实SubMesh有诸多好处,与没有SubMesh的Mesh相比,拥有多个SubMesh一样可以有动画,另外它还能针对不同部分的Mesh选择有个性化的材质球来表现效果,从功能上来看比单个Mesh要灵活的多。但它也有些许缺点,由于每个SubMesh都多出了材质球,导致SubMesh越多,增加的Drawcall也越多。Mesh中存在多个SubMesh,在动作和拆分材质球渲染上确实有很好的优势,但无法与其他Mesh合并,导致优化的一个重要环节被阻断。
SubMesh虽然功能很强大,但对性能的开销也需要注意,需要我们慎重使用。有时我们也可以选择用完全拆分Mesh为其他Mesh的形式来代替SubMesh,这样在合并Mesh时就有更多的选择了。下面我们就来深入浅出的聊聊合并模型的方法和途径。
动态合并3D模型。
我们制作的场景中的3D的物体很多,每个3D物体都需要有一个材质球支持,导致每个模型都会产生一个Drawcall(渲染管线的调用),众多的3D模型会产生很多Drawcall,CPU在等待渲染GPU在忙于处理Drawcall,使得帧率下降画面卡顿感强烈。
实际中的项目都会遇到这样的问题,场景中要摆放的3D物体很多,包括人物,建筑,路标,景观,树木,石头,碎块,花朵等。这些3D物体都有自己的材质球,相同模型的物体使用相同的材质球,不一样的物体使用不同的材质球,有时不一样的物体也有相同的材质球。如果不做任何优化处理就会产生很多Drawcall,导致帧率下降。于是我们就会想这么多的材质球引起这么多Drawcall,是否能合并合并成一个,这就是合并3D模型发挥作用的时候。
合并3D模型主要的目的就是为了减少Drawcall,它是通过减少材质球的提交数量来完成优化手段的,说的简单点就是把拥有相同材质球的模型合并起来成为一个模型和一个材质球,从而减少向GPU提交的Drawcall数量。
Unity3D引擎在合并模型从而优化Drawcall上有自己的功能,即 动态批处理 和 静态批处理 两种,它们的前提条件都是模型物体必须是相同材质球的模型,除了这个必要条件外还有其他条件也需要符合。下面我们就来介绍下Unity3D中动态批处理和静态批处理:
动态批处理
动态批处理即意味着随时都在做的模型合并批量处理,当我们把 Dynamic Batch 动态批处理开启时,Unity3D可以自动批处理场景中某些物体成为同一个Drawcall,如果是他们使用的是同一个材质球并且满足一些条件的话动态批处理会自动完成的,我们不需要增加额外的操作。
其中需要满足的动态批处理的条件是,
1,动态批处理的物体的顶点数目要在一定范围之内,动态批处理只能应用在少于900个顶点的Mesh中。
如果你的Shader使用顶点坐标,法线,单独的UV,那么只能动态批处理300个顶点内的网格,
如果你的Shader使用顶点坐标,法线,UV0,UV1和切线,则只能有180个顶点了。
2,两个物体的缩放比例一定是相同,假如两个物体不在同一个缩放单位上,它们将不会进行动态批处理(例如物体A的缩放比例是(1,1,1),物体B的缩放比例是是(1,1,2),他们的缩放比例不同则不会被合并处理,除非A的缩放比例改为(1,1,2),或者B的缩放比例改为(1,1,1))
3,使用相同的材质球的模型才会被合并,使用不同的材质球是不会被动态批处理的,即使他们模型是同一个或者看起来像是同一个。
4,多管线(Pipeline)Shader会中断动态批处理。
很多Unity3D里的Shader支持多个灯光的前置渲染增加了多个渲染通道,这些多个通道的材质球是无法用于动态批处理渲染的。
Legacy Deferred(灯光前置通道)传统延迟渲染路径已经被动态处理关闭,因为它必须绘制物体两次。
所有多个pass的Shader增加了渲染管道,不会被动态批处理。
动态批处理的条件是很苛刻的,在项目中很多模型是不符合动态批处理的。另外动态批处理要消耗CPU转换所有物体的顶点到世界空间的操作,所以它唯一的优势是如果它的工作能让Drawcall变少。
最后我们需要理解一味的减少Drawcall不是万能,它的资源需求取决于很多因素,主要被图形API使用。例如一个控制台或流行的API像Apple Metal这样的,Drawcall的开销会普遍很低,因此动态批处理时常在优化方面的优势并不是很大。
静态批处理
静态批处理允许引擎在离线的情况下去做模型合并的批处理以降低Drawcall,无论模型多大只要使用同一个材质球都会被静态批处理优化。他通常比动态批处理有用(因为它不需要实时转换顶点来消耗CPU),但也消耗了更多的内存。
为了让静态批处理起作用,我们需要将物体置为静态不同的,即我们需要去确认指定的物体是否是静态的不能动,不能移动、不能旋转或者缩放。因此我们需要给这物体在面板上标记一个静态的标记以确定性的告诉Unity3D引擎,此物体是不能动不能缩放的,可以对该物体做静态批处理的预处理。
参考资料:https://www.yxkfw.com/thread-70188-1-1.html
页:
[1]