如何在手机游戏中实现下雨系统?
先展示最终效果:
雨水会导致物体潮湿,会在平面形成水波,会在斜面形成纹路。雨会被物体阻挡,阻挡的部分依然保持干燥,干燥和潮湿有过度。水积累到一定程度还会有反射。
很早就想做一个完整的天气系统。不过难度太大了,所以打算拆分成一个个小系统。首先就是做最常见的下雨。
雨水本身实现已经有很多成熟的方式,包括我个人最喜欢的angrybots里面的雨,性能和表现都非常好。遗憾的是不够通用,所以我打算还是重新做一个分离的雨滴,方便后面加入一些中级效果,例如遮挡之类这样的特性。
我假定雨是有一个下界的,毕竟如果你潜入了水底,那么肯定就不希望会有任何下雨被看到。上界就不需要了,如果你高到一定程度,发现只有下方有雨,那估计也非常奇怪。
我们需要设计一种tile方式的雨来处理摄像机看到雨的范围。水平方向上可以调整tile个数,垂直方向我可以根据控制雨下落的生命周期来控制。
根据这张图,我们要计算出摄像机能看到的AABB盒,从而去做需要雨落下的物体的范围。
因为每个雨滴都是独立的,这次我们可以放心使用广告牌技术了。
另外,由于雨离摄像机太近会有点太大块的感觉,所以我建议将摄像机的nearPlane调整到1左右。
由于是局部雨,摄像机快速移动或者瞬间切换位置的时候,需要还原雨本应该的场景。 我们保存上一帧的AABB盒,并且对当前的AABB盒遍历,判断如果在原来的AABB盒内,那么就啥都不做,否则需要还原雨滴。
经过测试,发现虽然有一点点的衔接不够流畅的问题,但整体感受其实还不错。
不过上面做出的雨太干巴巴了,首先我要考虑的是增加雨滴打中地面的波纹。
波纹我们参考的是这篇文章:
https://seblagarde.wordpress.com/2013/01/03/water-drop-2b-dynamic-rain-and-its-effects/
其中涉及波纹的部分比较简单。
r通道代表离圆心的距离。gb通道代表法线方向,a通道代表随机强度值。
这样之后,波纹是有了。
远处可以优化下,太远的地方其实可以不用算,优化性能。然而这还不够,因为这地面看上去压根就不像湿滑的,湿滑的地面需要额外处理一下。通过增加一张法线贴图,控制一下uv的扰动,并且替换了下地表,来展示雨水的效果。
静态图效果不明显,还是看视频吧。
接下来要处理的是水导致物体本身的变化。一般颜色会稍变深,表面会变光滑,如果有水坑的话就会反光之类。其实就是pbr里面的smooth。但是手机用全套pbr性能有点耗,打算先把brdf3的一部分拿过来用。
做了一些修改。然后就是要考虑被阻挡的部分,我打算在顶部放一个摄像机,用来记录物体的深度,然后整个系统都需要去判断深度,然后看是否被雨水阻挡,再做相应处理。实际操作分为这么几步:
1.顶部摄像机用深度shader替换去渲染物体,然后做一下高斯模糊,用来让边界过度的更加自然。
2.平面shader把世界坐标转到顶部摄像机裁剪空间坐标,算出uv去取深度值,然后根据矩阵反算出世界坐标的值,比较两个世界坐标的y,来看是否顶部有物体挡住,如果有的话,就对潮湿效果,反射效果,波纹效果进行弱化处理。
3.反射效果就是用水面反射,增加一个反射摄像机,具体可以参考自带的水面反射。
效果大概是这样:
看视频效果更好一些。
手机上试了一下,效果全开的话帧率下降的比较厉害,然后去掉了反射效果,简化了光照模型之后,帧率得到了改善。
说下这个雨的注意事项。
我的本意是希望可以在不更改项目的情况下增加这个雨的效果,但我发现这样是需要做屏幕后处理才可以达到,但处理起来比较复杂。最终我还是选择了用Plane,Cube来做效果。如果要整合到自己的项目中,还是需要做一些代码上的整合。例如水导致物体潮湿,可以把我的函数和贴图放到自己的shader中,也可以直接修改shader里面的高光,光滑度等这些参数。
对于遮挡摄像机的部分,需要设置合适的大小和位置,如果场景特别大的话,通过移动摄像机的话,边缘部分会产生抖动。所以我建议大场景要使用多个深度摄像机或者自己提前烘焙好深度数据(如果场景没有动态障碍物的话)。
点击阅读原文,可获取插件地址。
↓↓↓点击阅读原文,了解更多。