浅析水面特效渲染
1. 水面效果
在游戏中,河流,海洋等水面效果的优劣一直是反映游戏画面品质的标杆。从最早的单机游戏到网游,水面效果越来越真实,越来越华丽。让我们先通过不同时期的游戏来领略一下水面效果的发展。
传奇
剑侠情缘OL的水面出现了波纹
天堂2的水面开始向实体进发
魔兽世界的水面效果
2. 水面的起伏
2.1 顶点网格
水面和地形本质类似,都是由网格构成的,不同之处是水面有起伏。所以将网格的顶点位置偏移,以模拟波浪上下起伏的形态是最直接的方式。
这种模拟有很多种方式,比如有简单的正弦波叠加
四个正弦波叠加模拟海浪
也有根据二维波动方程,来计算相对真实的流体。
根据这个方程的解y=h(x,z,t),可以得知在某个时刻(t),某个顶点(x,z)的高度值(y)。
各种相关学术论文的主要研究方向都集中在这里,以期得出更加精确高效更加优异的算法。
这种方式虽可模拟出真实的水面,但为了达到拟真的效果所使用的顶点数将非常惊人,由此带来的性能开销也很巨大。所以一般游戏比较少采用这种方法,就算使用,也是通过少量顶点简单模拟大范围起伏,精致的光影表现还是多靠后面的方式。
2.2 纹理动画
纹理动画是通过"纹理位图切换"或"纹理坐标变换"来实现的动画方式。
所谓纹理位图切换,就是将多张纹理图片,用帧动画的形式按时间播放。而纹理坐标变换,则顾名思义是单张纹理通过改变其坐标位置,产生动画效果。魔兽世界的水面效果,就是使用纹理动画完成的。
纹理动画对程序来说比较简单,但对美术的要求较高。因为它的实现方式决定了内存和容量占用较大,特别是用尽量少的位图来模拟尽量真实的水面,对美术的想象力和抽象能力都是一种大的挑战。
2.3 Bump Map
Bump Map即凹凸贴图,它的原理是在每个顶点计算照明之前,加上一个从高度图获得的扰动,这样便可以使光照的结果呈现出立体感。
Bump Map
现在,Normal Map(法线贴图)作为更高一级的技术被广泛地使用。凹凸贴图本质只是对现有模型的法线增加扰动,而法线贴图则会完全更新法线。凹凸映射通常根据一张单独的灰度图像通道进行高度计算,而法向贴图的数据通常是从更加细致版本的物体得到的多通道图像,即红、绿、蓝通道都是作为一个单独的颜色对待,然后再对应成x, y, z坐标。我们看到通常normal map都会以偏蓝色系的色调存在,这是因为蓝色直接对应着z轴方向,而大部分物体在坐标系中z值都为1。
Normal Map
法线贴图的另一个广泛的应用是,使用高分辨率模型的法向映射应用于低分辨率模型,这样可以大幅度地提高低模的显示效果,而同时减少性能开销。这项技术同时具有高品质的显示质量和较少占用资源这两个优势。
不过法线映射毕竟只是在光照渲染时修改法线方向,所以缺陷便是近距离观看时,会发现水面实际没有起伏。
3. 水面的颜色
3.1 反射
清澈的水体可以近似认为是透明的,我们所看到的水面的颜色主要由反射和折射共同组成的,比如海洋的颜色就是海水吸收了别的颜色而反射出蓝色形成的。先让我们从反射看起。
水面反射
如上图,A物体发出的光线有一部分到达水面(记为入射光线 I )后经过反射(记为反射光线R )进入观察者的眼睛,给人造成光线是由水面下A’发出的假象,由此形成可见物的倒影。根据光的反射定律,反射光线的反射角与入射角度相同,因此水面反射的公式为
R = I - 2N(N dot I)
当然我们在Shader中有对应的API,不必直接使用上述公式。在Shader中具体实现时,顶点函数中我们可以使用从Normal Map获得的任意一点的N向量,然后再用摄像机位置减去这一点的坐标得到入射光线I。
顶点函数
有了R向量后,我们在片段函数中,把他与环境贴图计算,便可得到所反射出的像素颜色。
片段函数
值得一提的是,环境贴图的生产有实时的和预渲染的。大多数情况下,使用预渲染的静态的环境贴图完全可以满足大多数需求,且性能比较可观。
添加了反射材质的水面图
3.2 折射
光的折射
光线通过不同折射率的材质时,会产生折射现象。这是因为光在教“浓” 的材质中传播得较慢的缘故。下面是光的折射公式:
代表介质的折射率。公式看起来很简单,但折射效果本身却是异常复杂,但从某种程度上来说,这种复杂反而是件幸运的事,因为这样一来,通过简单方法生成出来的图像很难被人们注意到异常。
具体实现时,折射与反射类似,也以水面为裁剪面来投影场景纹理,不同的是折射使用的环境贴图是水面之下的。
顶点函数
片段函数
3.3 菲涅尔效果
当光到达两种材质的接触面时,一部分光被反射出去,而另一部分光被折射,这个反射和折射的比率可以由菲涅尔公式表达,这种效果也被称为菲涅尔效果。这也是为何只有在几乎垂直往下看时,才能看到池塘底部的原因。
由于菲涅尔公式非常复杂,所以为了平衡性能,可以使用一个简单的快速方程:
fastFresnel = r + ( 1-r ) * pow ( 1.0 – dot ( viewDir, normal ), 5.0);
增加了菲涅尔效果的水面,再配合上高光,已经很有些模样了,对于一个不需要实时交互的水面来说,做到这一步已经可以满足需要而且性能尚可,我们的游戏在使用时基本可以保持50fps的帧率。
4.结语
上面简单阐述了水面特效的原理,作为一篇介绍基础的的文章,同时也是本人对shader初学的记录和心得,仅供初学者入门。由于本人水平有限,有纰漏错误之处,烦请斧正。
另外,特别感谢loren及gavin的指导,下面以《全民偶像 – 中国好声音》的水面效果截图向你们致敬。
(本文部分图片来自网络,版权属于原作者)