浅析水面特效渲染

发表于2015-11-12
评论0 5.2k浏览

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初学的记录和心得,仅供初学者入门。由于本人水平有限,有纰漏错误之处,烦请斧正。

另外,特别感谢lorengavin的指导,下面以《全民偶像 中国好声音》的水面效果截图向你们致敬。

 

(本文部分图片来自网络,版权属于原作者)

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引