最近几位网友在给我发信息说在Gamebryo中做水的渲染遇到很多问题,或者是无从下手;于是乎我打算在这里详细的
介绍一下在Gamebryo中整个水的渲染流程以及性能优化,希望对大家有所帮助,也希望大家能提出不正确的地方,或者
有更好的方案可以跟帖讨论和大家分享一下。同时也希望在转载的时候注明出处,谢谢。
一般水面渲染主要解决两个问题,一是如何实现波动,二是如何实现光照,选择什么样的算法,取决于你预期达到的效
果和可用的计算资源。对波动来说,游戏中最常见的做法是normal map扰动,由于顶点本身是静止的,这种方法只适合相
对平静的水面,优点是计算量小性能高;对真实的波形模拟来说,常见算法有FFT、Perlin噪声、Gerstner三种模型的海浪
仿真模拟。 ProjGrid,RTT,VTF属于实现某种算法的具体手段,需要先决定用什么样的算法,其次才是选择合适的实现手
段。 至于光照,对游戏来说,普通光照模型,加上用反射贴图,折射贴图菲聂耳效果混合就能做出不错的效果。
我这里就不介绍其他复杂水的实现方式,就我选择的实现方式来详细介绍一下,我选择的是目前3D网游最常见的实现方
式Normal Map加普通光照,再使用菲涅耳混合。首先是需要折射图和反射图,折射图和反射图其实就是用场景相机和场景相
机的反射相机各自再渲染一次场景到纹理,我这里使用到了深度图,我上一篇文章也介绍到了深度图,之前我是再渲染一次场
景到纹理,这次渲染到纹理的Pixel shader比较简单,只输出深度信息到纹理,这样的话算起来整个场景渲染相当于渲染了4遍,
还没加上阴影还需要渲染场景,这样看起来性能确实是一个很大的问题。后来通过优化在渲染反射和折射图的时候使用ClipPlane,
这样可以在渲染折射图和深度图的时候把水上的多余物体给裁剪掉,渲染反射图的时候可以把水下的物体全部裁剪掉。这是做
的第一次优化,当时性能稍微好了点,但是离可用远远不够的。然后我再去参照了GPU GEMS 2: Generic Refraction Simulation,
按照这种方式把折射图的渲染给省略掉了,但是性能还是没有明显的提升,因为本身水下的物体就相对比较少,最大的问题在
反射上面。并且当时渲染深度图的时候还存在半透明物体和透明物体和深度不准确的问题。于是再去寻找解决方案,后来又去
尝试使用Alpha Water,也就是通过顶点透明来来代替深度图和折射图,当时的做法是在场景加载的时候预先通过计算水面的
每个顶点垂直往下pick,返回pick的点,然后与水面做减法得到深度,把这个深度存到顶点信息中,在pixel shader中做alpha
混合,这样虽然省去了两张图的渲染,但是在场景加载的时候要做大量的CPU运算,加载时间长了很多,并且最终得到的深度的
效果很囧,斜着看浅的地方会看到深处的物体。
最后几经波折,尝试使用使用MRT来获得水面深度信息,MRT的好处是不用为了深度信息带来更多的性能消耗,在普通的
电脑上MRT的开销几乎可以忽略不计(大牛们请纠正,至少开销不大),并且深度信息十分精确。不过使用MRT带来了一个问题,
不能使用硬件全屏抗锯齿了,哎,真是囧啊,不过听说DX11可以支持Render Target的抗锯齿。后来我是使用的Nvidia的FXAA
来做的后处理抗锯齿,勉强可用,总比没有的强吧,所以这样这个方案就这样定下来了,接下来继续说一下对反射和折射。
折射和反射图跟以前一样直接通过渲染一次场景获得。不过在以前的基础上加上了人为的物体裁剪,我们项目使用了Gamebryo
的Toolbench这个工具(没有用这个工具的也可以用以下方案,只是实现方式不一样而已),在Toolbench中所有的可渲染物体的
Model都继承一个Renderable Model,这个Model里面有一系列自定义渲染属性,其中里面包含折射和反射属性,如果把反射或
折射属性设置成True则代表在游戏中这个物体能被水面反射或折射,这样在加载场景的时候监听场景物件的添加,如果有这个属
性就就给这个物体添加上反射或折射的标记,然后再把这个物体添加到场景管理器中去(场景管理器是自己写的一个数据结构树,
我是采用的四叉树结构,主要管理场景里面所有的可渲染的物体),在水面渲染的时候通过给水面添加一个自定义的NiCullingProcess,
这个裁剪处理器中直接通过场景管理器来获取当前场景需要反射和折射物体(我是使用了AOI来对摄像机区域做了缓存,第一次计
算AOI范围的物体,当没有离开AOI之前直接在这个列表里面去取,在取出的结果中筛选折射和反射物体),这样的话只需要美术
在Toolbench中自定义哪些要反射和折射,这样的话水的性能是由美术来决定的,在最佳的情况下折射和反射的渲染可能只有渲染
几物体性能消耗。在Toolbench中的Model有继承的功能,利用这点可以把树、怪物、玩家、NPC等默认设置成可以反射和折射,
然后再在离水远的地方把折射和反射关掉,这样折射和反射的物体少了,性能自然而然就上去了。我这里附上我做的水,在GT 210
128显存上,开了水和没开相差大概3-5帧的样子(总帧率30帧-35帧)。
再说一下水根据用户设定来调节性能,我在我们程序中设置了4个档次,最低的时候通过代码把折射和反射的渲染关闭掉,然
后再把水面的材质替换成美术提供的材质(水的本体可以直接让美术来绘制,在美术绘制的面上通过替换材质来实现水面的渲染),
这样的话最低效果的水就只是纯粹的一个普通模型,没有任何开销,第二个档次把水面渲染、折射、深度shader打开,折射贴图
使用512大小,第三个档次在第二个档次的基础上把反射打开,第四个档次在第三个档次的基础上把反射和折射的贴图改成了1024
大小。
差不多了,大的流程和方案我已经说完了,具体的细节肯定还是需要自己去完善和优化的。下一篇准备说一下关于Gamebryo
阴影的方案和性能优化。下面我附上几张我做的水的效果给大家分享一下:
Normal map的模拟波动
浅水的渲染
镜面水的渲染
太阳光的反射
转载请注明出处,谢谢!!!
本文地址:http://www.cnblogs.com/ivanzx/archive/2012/08/25/2656960.html
By 泥巴
参考例子:(这是我制作过程中收集到的例子,个人觉得已经非常全了,希望对大家有所帮助)
• http://www.vterrain.org/Water/
• GPU GEM 1:Effective Water Simulation from Physical Models, 2002
• GPU GEM 2:Generic Refraction Simulation, 2005
• Real-time water rendering - projected grid concept, 2004
• Hydrax
• Rendering Water as a Post-process Effect