Unity中的DrawCall

编程入门 行业动态 更新时间:2024-10-14 20:21:10

<a href=https://www.elefans.com/category/jswz/34/1771418.html style=Unity中的DrawCall"/>

Unity中的DrawCall

一:绘制原理

为了将物体绘制到屏幕上,引擎必须向图像API(例如OpenGL、Direct3D)发送一个DrawCall指令,每一次发送DrawCall指令的过程为一个渲染批次(Batch),而这个过程分为两大部分:设置渲染状态(setPass)和调用DrawCall(Batches),其中设置渲染状态属于比较重的分工,对于加载到游戏中的资源和对象等,CPU需要计算其顶点相关的矩阵,渲染所用的贴图,渲染所用到的材质和shader,渲染所用到的灯光等,如果每个物体的材质和贴图等都不一样,此时CPU的主要工作就是设置这些物体的渲染状态后调用DrawCall,从而造成CPU性能的开销,简单来说也就是CPU向GPU发送指令并由GPU进行绘制


二:DrawCall过多导致的结果

——程序卡顿
——耗电量快
——设备发热


三:什么是批处理?

因为CPU每一次发送DrawCall指令都会造成CPU的性能开销(DrawCall次数越多,CPU进行计算的量越大),而CPU的处理速度比GPU慢多了,所以可以将绘制的压力移交给GPU
Unity可以将一些物体进行合并,从而用一次DrawCall来渲染他们。这一操作,称为批处理
例如渲染一千个三角形,如果把它们按一千个单独的网格进行渲染则需要请求1000次DrawCall,而如果直接渲染一个包含了一千个三角形的网格则只需要请求1次DrawCall,这样减轻了CPU的计算开销在性能上会有很大的提升


四:渲染统计窗口


默认一个空3D工程,会有两个Batches,一个是天空盒,一个是相机
——Batches:相当于DrawCall次数
——Saved by batching:通过批处理节省的DrawCall次数
——SetPass calls:设置渲染状态的次数
只有在相机视野内的会被统计

在Unity中可以通过UnityStats类去查看DrawCall等数据,一般在移动端DrawCall尽量保持在200以下


五:3D场景优化

——静态批处理
静态批处理默认是开启的,Project Setting—Player—Static Batching
将需要静态批处理的物体设置为Static,游戏运行时会自动为它们合批,将所有静态物体的Mesh Filter自动合并成一个新的Mesh
可以使用工具类动态设置合并的对象:StaticBatchingUtility.Combine,这样就不需要勾选Static

注意:
——需要有相同材质,如果不同只会合并mesh,batches还是会增加
——静态合批的最大顶点数是65535,如果顶点数超过了它,Unity就会自动合并出多个Mesh
——运行游戏后合并过的Mesh对象是不可以发生位移的,但是可以移动父物体节点
——打包时会把合并的mesh存储起来,所以需要额外的内存去存储,并且运行时需要加载这个合并后的大Mesh,所以使用时需要注意,例如一个很大场景,使用静态批处理后包体中就会多一份合并的Mesh,运行时也会加载整个大场景的Mesh,但是游戏运行后只有一小部分出现在摄像机内,那么整个大的Mesh都需要参与渲染


——动态批处理
动态批处理默认是关闭的,需要手动开启:Project Setting—Player—Dynamic Batching

条件限制:
——必须使用相同材质(相同材质的物体之间的差别只在于顶点数据不同,可以将顶点数据合并在一起,再一起发送给GPU)
——Mesh不能超过900个顶点(带uv的模型不能超过300个顶点)
——transform的scale属性不能有负值
——多个pass的shader会破坏批处理


——GPU Instancing
GPU Instancing是指使用相同的材质和网格来渲染多个物体,但每个物体的属性(位置、颜色等)不同。这种方式适用于需要大量重复的物体,比如草地、树木等
勾选Material中的属性Enable GPU instacing,只有在支持GPU Instancing的shader才能选择

条件限制:
——必须使用相同网格和相同材质


——SRP Batcher 
只在UPR或SRP项目中有效,带有如MeshRender、TrailRender、LineRender、ParticleSystem、SpriteRender组件的mesh支持合批。带有SkinMeshRender和 布料模拟的mesh是不能合批


——减少实时光照和阴影效果
实时阴影会导致drawcalls大幅上升,建议关闭实时阴影,使用lightmap满足你想要的阴影效果


——代码动态合并网格
静态批处理会增加额外的内存消耗去存储合并后的Mesh,但原先没有用的Mesh仍保留在内存中,所以可以通过代码动态合并网格后将原先的网格销毁或隐藏

using UnityEngine;/// <summary>
/// 合并网格(挂载到要合并网格所有物体的父物体身上)
/// </summary>
public class CombineMesh : MonoBehaviour
{private void Start(){Combine();}/// <summary>/// 合并网格/// </summary>public void Combine(){MeshFilter[] filter = GetComponentsInChildren<MeshFilter>();MeshRenderer[] renderer = GetComponentsInChildren<MeshRenderer>();Material[] mat = new Material[renderer.Length];CombineInstance[] combine = new CombineInstance[renderer.Length];for (int i = 0; i < renderer.Length; i++){mat[i] = renderer[i].sharedMaterial;//获得共享材质combine[i].mesh = filter[i].sharedMesh;combine[i].transform = filter[i].transform.localToWorldMatrix;filter[i].gameObject.SetActive(false);}GameObject mesh = new GameObject("CombinedMesh");mesh.AddComponent<MeshFilter>().mesh.CombineMeshes(combine);mesh.AddComponent<MeshRenderer>().sharedMaterial = mat[0];}
}

六:UI优化(包含2D)

——2D的UI物体尽可能的安排同一个图集的物体在一起,如一个完整界面使用到的图片尽量打成一个图集,多个界面公用的图片打成一个图集
——UI元素打断合批必须是有重叠,所以注意不能合批的对象尽量不要重叠
——渲染顺序会打断合批,例如三个SpriteRenderer分别为A、B、C,A和B用同一个材质,C用单独的材质,正常来说,DC应该为2,因为A和B会合批,但是如果A的Order in Layer为1,B的Order in Layer为3,C的Order in Layer为2,那么渲染顺序就变成了A、C、B,C打断了合批导致DC仍为3

SpriteRenderer渲染顺序:Camera的depth>Z轴深度>Sorting Layer>Order in Layer
UI渲染顺序:
UI渲染顺序:Camera的Depth>Sorting Layer>Order in Layer>Plane Distance(Z轴深度)>层级顺序


七:其他打断批处理的因素

——调用Render.material也会创建一个新的material打断合批,应该使用render.shareMaterial保证材质共享

更多推荐

Unity中的DrawCall

本文发布于:2024-03-14 14:22:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1736662.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Unity   DrawCall

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!