查看原文
其他

如何在手游中实现云渲染的效果?

日音 腾讯GWB游戏无界 2022-08-30

先展示移动端云渲染的最终效果:


类似渲染云这种自然现象的时候,必须首先了解噪声这个概念。这个噪声指的是描述自然界规律的一些随机函数。例如大名鼎鼎的柏林噪声。Perlin噪声被大量用于云朵、火焰和地形等自然环境的模拟,而Worley噪声被提出用于模拟一些多孔结构,例如纸张、木纹等。不过其实Wroley噪声也可以用在云上面。


 
我们使用的噪声是iq大神弄的一个噪声,效率和表现都算很好。



这些还不够,我们还要分析下基本的算法,看下地平线早期的云实现:


主要思想还是根据raymarching得到云的外形,然后加上光照。

接下来先一步一步实现整个过程,但因为手机上要考虑性能,所以会对完整的云进行大幅度阉割。



中间红色的部分就是我们摄像机的FOV,那么可以这么计算出水平fov的tan。



实现以上所有的部分你可以得到一个普通的固定视角看上去还不错的云。类似于这样:



然而,如果你移动你摄像机的位置,你会发现这个云会出现各种异常。于是我们要一步一步解决问题。

首先是计算量的浪费问题,我们建立的云层模型是水平的,有顶部和底部组成,摄像机出发的射线,其实是经过顶部和底部的部分才有效果,那么其实我们可以直接从摄像机的顶部交点或者底部交点开始运算。如果位于摄像机里面,那么就是从摄像机位置开始运算。



这样子就可以解决第一个问题。

由于正常情况下云层一般较高,移动摄像机位置引起的变化量容易过大,导致一旦开始移动,采样贴图的坐标也迅速移动,导致云层瞬间变化。然而我们知道 ,摄像机的那点移动对于云来说不算什么,于是我增加了一个缩放系数,因为云层基本在几千米左右,我就把这个系数定位1000,然后根据和云层的距离再做一个基本的非线性关系。


可以看到,我都会根据顶部还是底部做不同的分支,这其实是我偷懒,可以用直线和线段求教简化这段代码,但现在就先这样,能实现功能优先。最后还有一个_cloudOffset和offset,我们后面再讲解。

接下来是边缘过度问题,云层的边缘超过顶部和底部会直接不计算,那么会导致一个难看的切边,于是我要根据顶部和底部增加一个透明过度。另外,如果云层很厚,我们将整个噪点分布在全部云层,会导致云层和稀薄,于是需要一个循环处理,我通过一个简单的线性周期函数来实现这个东西。


到这里,理论上应该可以了,但实际操作中发现离云很远的时候,云的噪点很厉害。想象一下在现实中,我们很远看云的时候,云的那些细节会逐渐变成较大块的纯色,边缘还是有细节。而我们根据位置进行那么远的采样,必然会导致都是噪点。我思来想去,觉得如果一致能维持一个较近的渲染效果,根据距离做一丢丢的变化,那就好了。于是我增加了_cloudOffset这个变量,会根据摄像机的位置和云的位置动态变化,保证云的渲染效果一直以较近距离观察为主。这样虽然在人靠近云的感受不太自然,但整体的感觉还可以接受。

最后,是光照。

这部分我没有太多想法,而是直接使用了iq大佬的基本思想,云的边缘会变量。通过往光线方向采样,得到新的位置的云的密度,如果密度变小,说明越靠近云的边缘,就更亮。

具体实现的时候依然根据云的上层和下层做了区别,因为如果一个方向变亮,那么另一个方向反而会变暗,所以需要反向一下y轴。


至此,我们大概实现了这样的云:



视频中还有一个穿越云层的效果,具体可以看视频内容。

最后说下这个云的问题。

这个云还远远没有到可使用的级别,第一,作为后处理特效,最早也只能在非透明物体渲染完成后,也就是云老是叠在非透明物体上。更合理的应该还是用CommandBuffer去做,可以控制渲染的时机。或者用一个面片去替代后处理,但是面片就无法降采样了,性能无法达到手机上的标准。所以还是需要用CommandBuffer改造这个云。

当然也可以自己设置深度缓存,这样子就可以实现云和物体的交融,但是性能当然就更耗了。

即便没有上述这些,这个手机场景demo在vivo x6手机上也只能跑30帧。那个穿越云层的大咖只有10帧。所以,还是需要更好一点的手机才能使用这种方式制作的云。

总而言之,还需要继续优化。

点击阅读原文,可获得噪声及插件地址。


今日推荐  

·手把手教你实现Unity网络同步

·《堡垒之夜》如何为多平台内容进行优化

·活动丨社区有奖征文:说出你的研发故事(第二期)

↓↓↓点击阅读原文,了解更多。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存