欢迎参与讨论,转载请注明出处。
前言
Demo的场景也到了做水面的时候了,在涉及技术之前首先要确定的是美术表达:当然大体上也就是卡通水与写实水的抉择,最终决定是做出《伊苏:起源》
那样的写实水(注重扭曲、透视、无形变),并在此之上现代化。
上色
首先我们先找个小池子作为试验场地——这样利于观测,那么很显然密室场景的熔岩就可以暂退了:
水面的本质很简单,它就是个面片而已(不论海浪)。最直接的第一步自然是上色:
上色之后自然是透视,把材质设置为Transparent
,调整下透明度:
很好,其实对于一些游戏的低画质,这个水面已经是成品了。当然这也太捞了,继续演进——
扭曲的准备
对于水面效果的重点自然是扭曲了,处于水中的部分都会因为光的折射而变化。当然我们实际做起来并不会遵照这些大道理,看着是那么回事就得了(图形学第一定律)。最简单的做法自然是把对象渲染完毕后的画面截获,水面材质再选取合适的画面部分显示,并基于此加入扭曲——
对于Built-In管线而言,想做到这点使用GrabPass
即可,这方面的实现在《Unity Shader入门精要》已有详细做法。可是由于其设计不符合SRP的哲学,在URP已经被毙了,于是我们只能另寻他法了。
当然实际上也没那么麻烦,思想已经有了,找到对应的实现方法即可:对象渲染完毕后的画面生成在URP可以通过管线设置文件勾选Opaque Texture
实现,然后便可在Shader声明_CameraColorTexture
调用。
当然仅仅如此会有个问题:此图的生成时机是渲染所有非透明(Opaque)对象后,对于具有透明度的对象(Transparent)的渲染时机是在此之后的,这样水面里将会看不到Transparent对象了。对于此有两个解决方案:
- 修改源码,将生成时机调到Transparent渲染之后。
- 利用
RenderFeature
自行在合适的时机生成画面Texture。
经过项目实际情况的考虑,我选择修改源码(具体修改在MyURP)。在Frame Debugger
可以看到渲染时机已经变为Transparent之后了:
做到这步只能算是准备好了子弹,接下来还要制造枪械:由于自带的Shader Pass的渲染时机并不在生成_CameraColorTexture
之后,所以我们需要利用RenderFeature
构建个渲染时机生成之后的环节。这里直接使用URP自带的Render Objects
即可满足:
如此只要Shader里Tag名为Grab
的Pass,都将会在此RenderFeature
进行渲染。接下来便是完成Shader:
|
|
Shader实现与一般形式十分相似,主要在于用上了_CameraColorTexture
以及ComputeScreenPos
函数,看看效果先:
看得出效果还是有所不同的,毕竟现在水面显示的不再是一层半透明蓝色了,而是原有画面的基础上调色。现在万事俱备,只欠东风了——
扭曲的实现
实现扭曲我们需要一张表达水面的法线贴图,或者噪声贴图也行。本质上是偏移UV,以产生扭曲的结果。我选择使用法线贴图,因为后续也有用到。
水面法线贴图的生产我并不了解,目前是随便找张不规则图形的基础上使用Unity自带的Create from Grayscale
生成的,效果居然还不错:
应用起来也很简单,获取法线贴图的xy数据加到screenPos.xy
即可。当然仅此而已的话水面是不会动的,所以我们还可以加个与时间挂钩的偏移值,以推动法线贴图的uv,便可产生动起来的效果:
|
|
不错不错,对于某些游戏而言,到了这步也算完成了。但还不够——
着色
目前有一个很明显的不足:虽然有了扭曲,但水面还是平平的一片蓝色,显然是缺乏明暗的体现。此时先前的法线贴图便可再次派上用场了:结合法线来做漫反射(Diffuse)效果。当然我们还不能直接使用取得的法线,还得将其转换至世界空间才行。
|
|
这里漫反射用的是半兰伯特(Half-Lambert),这是为了保证水面的亮度足够,看看效果:
嗯,有点味道了。再加个高光看看吧:
|
|
越来越有味了,不过感觉这种高光不够突出光点,加个Step试试:
|
|
不错不错,就这样吧,到实际场合看看。
反射
目前的效果如上,总的来说算是OK了,但感觉还是差了点什么……没错,就是反射。起初我很自然而然的脑补认为要让周边的岩石草木投射在水面,为此我尝试了各种方案(反射探针、反射摄像机、平面反射……)都不满意,最终发现这纯属脑补了。实际由于视角原因是达不到那样的效果的,能够反射的内容基本会与折射重叠。醒悟之后发现最合适的反射内容只有纵身跳入的人物以及天空罢了:前者的出现场合太少了,对于后者与其用各种反射手段,还不如直接弄张天空贴图完事。
|
|
弄了张天空贴图,结合扭曲所用的偏移值进行uv移动,使用_Fresnel
控制反射与折射的比例。注意这里的_Fresnel
仅仅是个0-1的参数,并非是真正的菲涅耳系数(由于视角关系根本用不到)。来对比下吧:
这样的假反射在美术上的意义主要是能让水的颜色没那么单调,并且由于贴图是移动的,也带来了更多的动感。
后记
最后加上点互动特效,有那么点意思了:
在加这波粒子特效时也遇到了不少问题,也多了一些想要实现的东西。限于篇幅只能留待日后了。