《天涯明月刀》中的雨景渲染

发表于2015-04-29
评论1 1.58w浏览

天涯明月刀ol天气效果视频:

http://v.qq.com/page/b/z/f/b0012wadazf.html?start=17

http://v.qq.com/cover/z/zyeg1tfa935xma0.html?vid=z0012ankmbw

 

天气系统可以带给玩家强烈的游戏带入感,可以使游戏显得更上流(咦?),于是大家纷纷都有了。下雨是天气系统的重要组成部分,大多数游戏是这样表现雨的:

http://km.oa.com/files/post_photo/238/169238/575d2fd7630506cf0501a18a889bb2c3.jpg

天涯明月刀ol的画面表现定位很高,可以说是倍受风景党们期待的一款游戏。如果我也这样做一个雨……

这种下锥子的感觉是怎么回事,考评不会拿C么?

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image002.jpg

真实的雨氛围应该是什么样的呢?

 

l  雨要绵密,你数不清有多少雨丝,但是可以判断出是大雨还是小雨:

  http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image003.jpg

 

l  地面会潮湿,泥泞,反光增强:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image004.jpg

 

l  路面有积水和水花:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image005.jpg

 

l  水面上有涟漪:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image006.jpg

 

l  天空与场景变得灰暗,起雾、能见度降低

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image007.jpg

 

l  乌云密布电闪雷鸣

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image008.jpg

以下就是在QS-Game中,为了营造一个能让玩家沉浸其中的雨景,我做过的工作。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image009.png

 

1 雨丝渲染Raindrops

雨丝是雨景渲染工作中最重要、最复杂也是研究资料最多的一个,通常有两种作法。

 

1.  最常见的一种是用粒子模拟,效果见图1、图2。他的优点是:每个雨滴实际存在于3D场景中,所以雨的运动轨迹容易控制,随风倾斜,视角变换都很容易实现。缺点是:粒子数量不能太多,否则影响性能,这样就无法做密集的雨。如图2,用了10000个粒子,依然感觉很稀疏。但是又想营造出倾盆大雨的效果,怎么办呢,那就加长每个粒子吧,结果就这样显得很生硬。

 

2. 另一种方法是在屏幕空间做一个纹理动画,他与粒子雨的优缺点正好颠倒。纹理动画是一个后处理,表现任意降雨强度都是相同的性能开销。但由于他不真正处于3D空间中,雨的倾斜就做不了,在任何视角,雨在屏幕上的运动方向都是相同的。例如在ATI Toy shop demo中使用四层纹理,每层以不同的速度运动,以模仿降雨的纵深感。但他没有解决视角问题,只是通过锁定视角范围,掩饰这个破绽。仔细观察,在看向地面时,降雨方向将近要与地面平行,QS-Game几乎是全视角,显然这种方法也不适合。

 

为了表现密集、真实感强的雨,我选择用纹理模拟的方法,这就要解决几个问题:雨的方向与视角变换;深度视差;场景遮挡。

 

最终,我用类似Flight Simulator 2004(微软模拟飞行)里的方法解决视角问题,在3D空间中创建一个椎体包围相机,在椎体上做纹理动画,通过调整椎体的倾斜度表现雨的方向,还可以通过增大椎体倾斜和拉长、加快纹理动画表现出镜头移动加速的效果。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image010.jpg

 

椎体的上下两个顶点解决了向上向下看的问题,改变两个顶点的顶点色,使俯视、仰视时的效果更自然

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image011.jpg

 

深度视差,就是近大远小的问题,参考Toy shop demo,使用四层纹理,表现远近,越远的层纹理tilling值越大,运动速度越慢,而且每层的动画方向稍有不同,表现出雨滴运动的不规则、交错感。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image012.pnghttp://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image013.jpg

 

屋子里不能下雨,就要解决雨与场景物体的遮挡问题。用雨纹理的一个通道存储雨滴的线性深度值,这样在pixelshader里就能计算出雨滴在3D空间中的虚拟位置,然后类似阴影的作法,用一个正交摄像机顺着雨下落的方向,渲染一个深度图。深度图不用很大,256的精度就够用。在pixelshader里把雨滴的虚拟位置转换到投影摄像机空间中,然后雨滴的深度与深度图中的比较,判断出雨是否被遮挡。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image014.png

Pixel Shader:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image015.png

 

2 潮湿

简单的,通过增强材质的Gloss、Specular,加深diffuse,可以得到一个不错的潮湿感觉

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image016.jpg

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image017.jpg

 

3水花渲染Splash

 

水花是用GPU粒子做的,并在粒子上做一个帧动画。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image018.jpg

粒子不可能铺在整个场景中,只需要在相机周围一定范围内出现。但是由于GPU粒子位置计算是使用一个初始属性和当前时间定义的闭合函数,所以粒子系统位置不能一直跟随相机,可以用九宫格方法解决这个问题。如图,创建9个粒子系统环绕相机,当相机走入红点位置,将后面的3个粒子系统移动到前面。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image019.png

 

用九宫格的方法在相机周围铺上一层水花粒子,在vertexshader中,利用上面渲染的碰撞深度图,用类似的方法,由深度计算出碰撞点的位置,就是水花应该在的位置,如果超出深度图范围,可以把水花移出场景坐标外。

 

4涟漪

涟漪渲染是参考的Game Programming Gems1里的方法,不过书上的渲染方法是在cpu中通过下面的公式计算得到高度,然后变形网格渲染。而我把计算过程放到GPU中,把涟漪起伏渲染成一张高度图,再通过高度图得到法线贴图,最后作用到水面渲染上。

 

这个是水波高度计算公式,是一个近似简化公式,推导过程略。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image020.jpg

c表示波的传播速度,h是点间距,GPU中一个像素对应一个计算点,所以间距定为1,Δt是帧间的时间差。

公式的意义为:计算点在当前帧的高度 = 系数A*(上一帧周围四个点的高度和) + 系数B*计算点上一帧高度 -计算点上上一帧高度

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image021.png

从公式可以看出,计算当前帧某点的高度值,需要前两帧的高度数据,可以使用两张G16R16F RT,渲染一个RT时,采样另一个RT,得到上一帧和上上帧的高度,然后保存当前帧与上一帧的高度到渲染的RT上,作为下一帧的输入纹理。

 

渲染一帧的步骤

1.         随机渲染一些白点,作为波源

2.         通过上述方法渲染高度图

3.         通过高度图得到法线贴图

4.         法线贴图作用到水面渲染上

 

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image022.png

可以看到渲染出的涟漪不够圆,有点偏方,如果计算上一帧周围八个点的高度和,可以改善这个问题,但是效率会下降。动态时偏方这个问题不会感觉很明显。

 

5积水效果

水平面积水:把上面的涟漪法线作用到材质上,并稍微扰动底色。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image023.jpg

 

垂直面流水:静态图不明显,有水流下石头的效果。同样的使用一张流水法线贴图,作用在表面上,并稍微扰动底色。

http://top.oa.com/pictures/201307/1374834481_4.gif关键是shader中如何区分水平面与垂直面

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image025.png

 

6雷电

雷电的形状要千变万化,扭曲的要风骚。实现复杂度与效果综合考量,决定使用分型法。

 

算法的基本思想是:首先给定一根直线,取直线中点位置,在垂直于直线的方向上随机偏移,连接起点与偏移后的中点、偏移后的中点与终点,生成两根直线,在第一根直线的方向上随机位置生成一根枝杈,在这三根直线上重复上面的步骤,不断迭代,直到结果满意为止。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image026.jpg

 

生成的闪电形态如下:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image027.png

 

然后每一小段生成一个始终面向摄像机的矩形用于渲染,有一个问题是,当闪电比较粗时,交界处会很明显

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image028.png

 

可以这样解决:每一段延伸出宽的一半,然后pixel shader中衰减边缘。

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image029.png

闪电效果图:

http://avocado.oa.com/fconv/files/201307/94b49cb40b49e0c484138f4b22b584fa.files/image030.jpg

 

参考资料:

强烈推荐

https://seblagarde.wordpress.com/2012/12/27/water-drop-2a-dynamic-rain-and-its-effects/

 

ATI ToyShop demo

 

微软模拟飞行中的雨雪渲染

http://ofb.net/~niniane/rainsnow/

 

NVIDIA SDK 10 Lightning

http://developer.download.nvidia.com/SDK/10.5/direct3d/samples.html

 

Game Programming Gems 1:2.6 Interactive Simulation of Water Surfaces

 

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