这次先来讨论一下网格模型的问题,贴图和合并渲染等问题下次再来讨论。
首先看看这样一个场景:
这个场景的效果比较不错,但作为手机游戏的场景来说性能并不好。
经常会听到有朋友在议论游戏面数的问题,比如说某个引擎支持百万级别的面数。这里我们需要先搞清楚一个概念,我们讨论的显示面数,一般是指同屏渲染面数,而且是指的三角面的数量。就像上面这个场景,从Stats里面看,他有401k的三角面(Tris),也就是40万个三角面了。
显卡渲染一个三维画面,原理是先用摄像机的视锥的参数对场景里面所有模型数据做一个裁剪,确定摄像机当前能看到的范围,然后把顶点和索引数据、UV坐标和贴图传进显卡,然后显卡根据顶点和索引数据逐个三角形绘制出来,然后根据UV坐标给三角形赋予贴图,从而组成了我们看到的三维模型。最后再叠加各种效果,比如灯光,需要通过光照模型计算受光面和阴影各自的角度来确定模型在贴图和灯光叠加后应该有的颜色,比如实时阴影,会对模型再次的渲染并计算矩阵,把影子的范围和颜色叠加在模型上。再比如一些后期的渲染效果,会把渲染后得到的画面,再次的加工。举模糊效果为例子,很多模糊效果实际上是渲染多个偏移的画面,然后做叠加。所以这些效果都会产生多次渲染从而增加实际显卡渲染的三角形数量。
上面那个场景,实际的模型面数都很低,比如
一个模型也就400多面。当然还可以减得更低的面数,具体的细节保留要视乎游戏实际的观察角度和举例而定。
在画面里面同时看到的模型并没有想象中的多,那么可以肯定这个场景实际上是开了不少的效果的,比如灯光,比如后期渲染。接下来我们把灯光关掉,把所有的后期渲染特效关掉,就得到了以下的一个场景:
实际上这个场景渲染的三角面只有104.4k,也就是10万个三角面。
这个效果比起刚才一开始的效果就会差了很多,而实际导致面数激增的原因,是因为场景里面打了直射灯光。所以一般在手机游戏里面,打实时灯光是一件比较奢侈的事情,一般会使用烘焙LightMap的手段,保证场景的光照效果,又不会产生很多的渲染面数。
怎么衡量手机上面的同屏面数呢?虽然现在的3D游戏引擎基本都是超过百万级别的面数支持,但实际上并不是所有的手机都能跑得起这么高的面数的。以现在市场占有率比较高的一些中低端安卓手机来说,10万面是一个分界线。同屏10万面以上,很有可能就会出现比较明显的掉帧情况。 上面这个场景,虽然是同屏显示10万面,但实际上游戏里面还会包含很多角色类的模型,还有特效之类。所以这个场景实际上还可以继续的减面,理想的状态下最好是同屏6万面左右就够了。
刚才说的是固定视角的同屏显示面数,如果是可以控制摄像机角度的游戏,那么摄像机的视域(fov)参数和远近端裁剪的数值就起到比较关键的作用。
还是同样的场景, 我们把视域调大一点(原来是50),然后把摄像机拉高一点,渲染的面数就变成了12万了。
减少这种由于摄像机变动而产生的面数,有很多手段,比如遮挡剔除,或者Lod,或者手动计算可视范围,把范围外的模型主动的隐藏,等等。
讨论完同屏渲染面数的问题,那我们是不是可以在场景里面无限量的放置模型,然后 只要保证它们不渲染就行了?
其实并不是的。除了渲染的压力,同屏的面数过高还会产生内存上的问题。一个三角面包括了三个顶点信息,三个UV坐标,三个索引数字,如果有LightMap,还会有UV2信息,如果用到顶点颜色,还会每个顶点多一个color的rgba值。当面数多起来的时候,这些东西都是会占内存的。
上面这个场景的Meshes资源占用内存是23.4M,还算合理范围内。
在这些内存里面,一部分是fbx里面带的网格的数据,只有5.2M。实际上网格模型数据对比起贴图数据是小很多很多的。
另外一部分的内存消耗,是在场景物体上。上面的场景,为了静态合并渲染,所以勾选了Batching Static选项。这样在实际运行中,Batching的数量和SetPass的数量会降低,但会产生额外的内存消耗。因为静态合并网格的原理就是把现有的场景内模型进行合并,让多个模型变为一个新的网格模型来渲染。合并的规则把尽量多的模型合并,直到超过65000面(Unity引擎单个Mesh最高支持65000面)。然后超过的部分另外起一个新的mesh,直到达到65000面。
像上面的场景,它就合并了三个Combined Mesh,总共占了17.7M的内存。
最后一个消耗,是推到显卡里面的显示数据占的内存,实际上网格模型占这个消耗的比例很低,远远不如贴图占的。
所以,如果无止尽的往场景里面放资源,就算你把它们都隐藏了,但内存还是会很高的。当你的场景非常非常大时,比如做无缝地图之类的,必须注意根据显示的规则去把一些已经隐藏了的模型做销毁处理,不然你的游戏在手机上面轻易的超过几百M甚至1G的内存。那么市面上大部分的手机可能都会闪退了。
评论