移动平台粒子系统的实现原理

来源:互联网 发布:37诸神黄昏进阶数据 编辑:程序博客网 时间:2024/05/17 03:47

粒子系统在游戏中的应用可以说是非常常见。几乎每个游戏都会用到粒子系统。粒子系统可用来表现游戏中的各种效果,比如打斗的刀光剑影,飞船的尾气火焰等等。

其实一个简单粒子构成是由面片组成,在游戏渲染中多数粒子采用的是四边形(也就是两个三角形组合而成)的面片,然后贴上对应的贴图。然后根据游戏系统需要,每帧更新粒子的矩阵和纹理UV,来达到粒子移动和切换。当粒子数量达到一定程度加上动画效果。效果就会非常好看。

如图:


此为Unity3D中的粒子效果。可以看到每个粒子就是由两个面片组成。

在游戏当中,我们往往使用的是3D视角,而面片是2D的,如果没有对着镜头,那么效果就会穿帮。

因此我们面片需要使用公告牌的效果,也就是在每次更新的时候时刻旋转粒子面片朝向使其正对相机。一般来说公告牌会分成两种,一种是无论镜头如何旋转,粒子XYZ三轴都会旋转使之面对镜头,另外一种则是仅仅应对旋转绕着正上方的轴旋转匹配(一般是Y轴)。这个计算方式可以获取相机的信息,然后跟着反向旋转来达到一直面对相机的效果。至于是三轴对齐还是单轴对齐则取决于游戏中场景的需求。


一般使用上面的方式可以实现一个粒子系统。但是粒子系统还有一个很重要的考量,那就是渲染效率。这里要提及一个叫DrawCall的概念。一个DrawCall对应是显卡渲染一次三角形命令。而在这一次的命令中渲染的是一个三角形还是100个都算是一次DrawCall。频繁的提交三角形渲染命令对渲染效率的影响是巨大的。在2006年时候GPU GEMS 2的年代中,提及了PC端机器最多只能承受几千次的DrawCall,否则就会导致帧率卡顿。而在移动平台上,iphone4的水准是要求DrawCall不得超过80,即使现在2017年的硬件,达到200多的DrawCall哪怕场景再简单也会卡顿。因此如果粒子系统每渲染一个粒子就提交一次DrawCall这明显是非常不合理的做法,直接表现为整体卡顿无法使用。像各种商业引擎甚至自制引擎,都不会这么做。一般会使用合并渲染批次的做法,也就是预先分配一块巨大的顶点缓冲块,根据能够合并的粒子一帧最多可能渲染的粒子个数创建一块巨大的缓冲区。然后把需要渲染的粒子进行排序后放置在缓冲区中。然后每次指定所需渲染的粒子个数。这样当粒子只有一种的时候,哪怕上千个粒子,其DrawCall也可以合并成一个。Unity中最明显表现在每个粒子发射器只占了一个DrawCall就是这么做出来的。


粒子批次合并需要对所有粒子的和摄像机位置进行排序,因为粒子一般都是半透的,需要从远到近进行排序渲染,否则blend会出现错误的效果。可以获取粒子的位置和当前相机的view transform进行叉乘计算。计算出视觉空间的距离。



粒子合并成批次之后渲染会有个问题,那就是zwrite必须关闭,ztest必须打开, ztest如果不打开 无法在3D空间和其他3D物件渲染出正确的前后遮挡关系,zwrite关闭时因为打开了ztest之后如果还zwrite,当粒子不是刚好覆盖4边形的时候,镂空处会莫名其妙地遮挡了后面的粒子,这个效果会非常诡异且无解。也因为这个原因 粒子一般都要放到场景渲染完成后再去渲染粒子,以保证正确的遮挡关系。

原创粉丝点击