卷轴游戏中的水体渲染



水体渲染是游戏制作中永恒的话题之一,而在奥日系列、毛线小精灵系列(Unravel)、三位一体系列(Trine)这类的卷轴游戏中,由于视角的特殊性表现上也会有所不同。因为最近刚打完奥日2而且本人也在制作卷轴游戏中,所以粗略实现了下类似的效果,下文中即涉及到一些成熟的技术也有自己想的trick,希望对大家有所帮助,有不足之处也望得到大家矫正。


!) 本文涉及到实现方法大都并非卷轴游戏特有,做正常视角的3D游戏也可以放心看下去!!!

!) 伸手党可以直接去文末下材质和蓝图文件!!!

!) 下面的顺序是纯粹的个人制作顺序,没有任何逻辑关系!!!


·准备模型

主要是为了给顶面和侧面分配不同的材质,在正常视角中实现这类效果可以使用计算近裁面的方式,参考官方论坛中ryan的回答


·添加顶面反射

用反射方向采用cubemap添加简单的反射。



·添加雾效

添加了一个简单的线性雾,有高要求的可以上指数雾高度雾啥的。



·添加分层的光照介质

添加光照介质的本质是制作面片光的方法,但是希望可能满足以下需求:

  • 不能附着在面片表面上
  • 在空间上有确定的位置,不会随着相机移动变化
  • 和物体会有遮蔽关系,不会在奇怪的地方出现光照
  • 为了满足以上的需求,我从水面往里一定的深度距离上构建一个虚构的平面,通过计算距离得到当前像素在虚构面上的世界坐标。


    用得到这个世界坐标在单轴上略微偏移去采样光束。再用世界位置去反算出在相机空间的深度去做depth fade来实现遮蔽效果。

    这边展示只用了一层,最后使用是用了两层会更有层次和深度的感觉。后续可能会再添加上噪波影响。

    ·水面交互

    水面交互因为看错论文花了很长时间hh,主要是实现以下几点:

  • 正常水面涟漪的扩散
  • 涟漪接触边缘时的反弹效果
  • 贴图会跟随角色位置更新
  • 计算上主要的参考是NVIDIA的Fast Water Simulation for Games Using Height Fields ,主体是牛顿第二定律f=ma的另一个版本a=c*c*u,就是加速度=曲率x速度平方。


    用上述公式得到曲率后就可以得到加速度,然后v+=a*t,h+=v*t就可以得到新一帧的高度了。

    谈一下具体实现中的问题:

  • 由于在虚幻中添加cs十分麻烦所以还是使用绘制RT的方式
  • 使用了RG16bit的格式,R通道储存高度,G通道存储速度
  • 由于高度和速度都存在负值情况,所以在初始化和绘制时都要偏移0值
  • 边缘碰撞反弹的效果我尝试了下nv的ppt里后面的公式没有成功(现在想来应该是当时做随角色偏移写错了),后来去搜别人webgl里的实现 ,然后发现完全没有做反弹的处理就可以有边缘的bounce,猜测是贴图clamp模式的问题,于是在shader中实现手动clamp,伪代码如下:

  • if (neighbour!= block)
  •   sample(neighbour).g//**g is velocity
  • else
  •   sample(this).g
  • 随角色偏移的部分是出于性能考虑,即使是在大世界里也只需要绘制这一张就可以了,这边没有采取近期某篇水体画到另一张rt的方式。 主要实现是把角色的偏移值从世界空间转到rt绘制的uv坐标再在采样时进行偏移就好了。需要注意的是这类的方法在绘制很静态的轨迹(例如雪迹)时 需要特殊处理采样时不满一纹素的小数部分,这边因为水体会扩散并且会快速消散所以基本无法察觉。


    ·添加Caustic

    Caustic本想是做基于sdf和path tracing的方法配合高度场的,但是仔细看下计算量和考虑虚幻中距离场质量还是放弃了。 最后用了最近雷火也有推到的Dave_Hoskins大佬的2Tweet Water Caustic

    emm...

    虽然只有短短几行代码,但是完全看不懂是不是!!因为看评论里已经被大佬们砍字节砍惨了。 我尝试理解这个shader并重写了一份更nature的“人话版”,去掉了sine造成的缩放问题。

    然后是uv输入的问题,直接还原场景坐标从z轴平拍的话会拉伸比较严重,所以可以往灯光方向的平面上投射了下坐标。

    最后利用gbuffer中的世界法线dot灯光方向来剔除掉一些背面的光。


    灯光方向朝右,左边无光右边有光


    ·TBC

    另外有些别的效果比如水的透视并没有加进去,因为觉得可能会影响卷轴游戏的体验。

    最后献上蓝图和材质文件。(应该会少两张贴图随便给个noise就好了,另外本来想着蹭下奥日的热度做的比较急,也没整理优化啥的可能有不少重复计算,见谅)