【Unity】基于顶点色的海边波浪效果(适用移动端)

之前曾做过一个unity中带有海边波浪冲刷的海水shader效果,并最初发布在CSDN上,原文链接: http://blog.csdn.net/mobilebbki399/article/details/50493117
不过最初的效果原先仅仅是自己自学shader与练习时实现的,没有考虑过多优化措施,总的来说做的比较水(当时才工作半年多 ~_~|||)


考虑到该效果貌似关注的人挺多的,最近刚好也在项目中使用了该效果的移动版本,因此整理出了Demo分享一下制作经验(Demo Git地址在最下面):


实现过程:
1.使用顶点颜色代替深度图
在原版的效果中,我使用了投影深度图的方式,来计算海水底部到海面的深度差,通过这种方式判断海水的潜水处和深水处,这是一种很常见的实现水体效果的方法,然而这意味着:首先我们需要额外渲染场景的深度图,这个操作对于PC平台而言消耗不大,事实上unity中PC平台默认就是开启深度图渲染的,而对于移动平台,深度图的渲染是关闭的,我们当然可以手动在代码中设置开启,但这意味着额外的drawcall消耗。

因此我考虑使用顶点色的方式来表达海水的深水和潜水处,如下图:

但使用这种方式需要高度依赖海水mesh的顶点数量和顶点分别,以及顶点色的控制,因此存在以下两个问题:
1.绘制顶点色的成本
2.如何控制顶点数量和分布

对于第一个问题,我的实现方式是,在unity中通过编辑器预先从海水mesh顶部往下渲染深度图,并映射到mesh的顶点色,同时增加工具允许美术直接在unity中直观的绘制顶点色,如下:
该纹理在编辑器中渲染并映射到mesh:

提供直接绘制顶点色的工具用于调整效果:



比较棘手的是第二个问题,考虑到如果直接交由美术制作mesh,一方面增加制作成本,另一方面比较不容易把控顶点的分布于数量,因为显然这种基于顶点色的效果依赖顶点数量,但顶点数量过多会增加mesh占用的内存以及渲染性能,而直接由程序生成mesh的话如果顶点过少又无法达到效果,如下:

过少的顶点无法体现出海水波浪效果:


2.Mesh细分的思路
为了解决上述为题,我实现了两种对Mesh根据海陆交界处细分的方法,实际上这两种方法都大量借鉴了比较流行的地形lod技术中的方法(只不过我生成的是静态mesh),其中第一种方法借鉴了GeoMipmapping技术,第二种方法则依靠四叉树来实现:

第一种方式借鉴了GeoMipmapping技术:https://en.wikipedia.org/wiki/Geomipmapping
这是一种常见的地形Lod技术,如下,将地形分成NxN个格子,每个格子给予一个lod值,该lod一般根据视点与该格子的距离计算得到,根据不同的lod决定该Lod的细节:


在地形系统中,一般格子的lod是根据视点与该单元格的距离决定的,距离越远lod越大,网格细节越少。而我这边则是对于接近岸边的Mesh,给予最高Lod(为了方便起见,我这边是最高lod才获得最多的细节),将获得最多的顶点数量,而远离岸边的水域,由于不需要过多细节,则给予较低的Lod,只会产生较少顶点,同时对于会被陆地遮挡的部分,则根本不产生顶点,这样即可保证岸边的浪花具有更多细节,如下改进后生成的网格:


被陆地遮挡部分不会产生多余的三角形


主要原理首先在渲染的深度图中额外增加一个通道渲染陆地和海水的交界处,然后将贴图划分成指定大小的网格,通过判断每个网格像素的极差简单判断该网格是否位于海水与陆地的交界处,如下标红点的网格即为交界处:


交界处的网格将获得最大Lod,和它相邻的网格的Lod则依次递减。

比较麻烦的地方在于不同lod的网格如何进行衔接,我的做法是根据不同lod的网格之间的衔接,网格中会发生变化的只有边缘处的三角,且只有低Lod与高Lod衔接时需要进行衔接处计算,因此容易找到衔接处的规律:

这种做法仍然存在不足之处,首先网格中的海水部分和陆地部分的像素占比是不同的,仅依靠极差并不一定准确,如下,蓝色箭头所指的网格实际上仅有一小部分为陆地,但仍然会分配到最大Lod,而紫色箭头所指的网格尽管完全位于海水处,但靠陆地非常近,却依然无法得到最大Lod:



第二种方式则是考虑使用四叉树的方式,先根据划分的最小单元格作为叶子节点(单元格数量只能为2的n次方个),并标记处位于海陆接缝处的节点:


然后进行递归,将每四个节点合并为一个新节点,对于四个节点都不为边界处网格的情况,可以合并为一个完整四边形,如下:


最终可以生成更少的顶点:



GitHub地址请访问博客原文:http://www.lsngo.net/2018/03/22/unity_seawave_vertexcolor/


你可能感兴趣的:(Unity,游戏开发,Shader,图形与渲染)