这一期,我们继续制作卡通水面的深度变化效果和泡沫效果。
在制作之前,我们先调整一个设置,找到你Render Pipeline的设置文件,不知道在哪的,你可以打开菜单里的Edit→Project Settings,看到Graphics→Scriptable Render Pipeline Settings,点开那个文件
然后看到Inspector面板,把General里面的Depth Texture勾选上
我们制作深度效果的基本思路,就是让水面的颜色根据深度在两个设定好的颜色之间过渡。比如,我们有颜色A和B,当深度是0的时候就显示A,当深度到达某个值以后的时候就显示B,而之间的情况就是两种颜色的混合。在Shader Graph中,我们有一个叫做Lerp的节点可以实现这样的效果。
我们找到在上一期里,与PBR Master连接的Add节点:
我们把Add和Albedo的连接断开,然后新建一个新的颜色Property。我这里给它取名为Deep Color,也就是代表深水区的颜色。
我们新建一个Add节点,然后把之前的Multiply的结果和Deep Color都给这个新的Add节点。
这样我们就得到了共享同一个波光效果的两个不同的颜色。
这里大家可以看到,两个Add节点的A和B的连接顺序是不一样的,上面是A颜色,B波光,而下面相反。我是为了让图看起来更加规整一点而反过来连接的,因为实际上Color的加法也是满足交换律的,正反不影响结果。
我们新建一个Lerp节点,把两个Add和Lerp的A和B连接上。
我们可以看到Lerp有三个输入,前两个很好理解,后面这个T,就是用来控制它在A和B之间过渡的。T小于等于0就是A,T大于等于1就是B,一般我们把T控制在0到1之间就好了。
那么现在我们就需要得到水的深度,并把这个深度转换成0到1之间的数值。如何得到深度呢?这里我准备新建一个Sub Shader Graph,也就是一个能够嵌入Shader Graph的Shader Graph,俗话说,套娃。
为什么要套娃呢?因为获取深度信息并把其限制在0和1之间的节点并不少,而且之后我们还会要多次重复的使用同样的一组节点,如果把这些节点打包成一个Sub Shader Graph的话,我们之后就不需要重复的制作获取深度的功能了。
然后我们来看一下,我们要获取的深度具体是什么。如下图所示,我们要获取的深度不是水面与地面的垂直深度,而是从我们的视线出发,看向水底的深度。
获取视线深度而不是垂直深度,是因为光线传播的问题,如下图:
在现实中,我们看到的从水中反射折射出来的光线,在水中经过的路程越长,能量损耗越大。但是同时,水面直接反射的光线与反射面的法线夹角越大,能量损耗越低。不过我们这个卡通水就不用讲究那么多了,而且我们制作的还不是光追渲染的Shader,我们其实根本获取不到光线的射线,所以我们就忽略水面直接反射的光线就行了。其实我们后面慢慢调整数值也能实现不错的效果。
那我们如何获取这个深度呢?Shader Graph 为我们提供了两个节点,分别是Scene Depth(场景深度)和Screen Position(屏幕坐标),他们分别代表:
我们将Scene Depth减去Screen Postion的深度,也就是Screen Postion的w,就可以获得深度了。
这里讲解一下Screen Postion这个节点,这个节点会返回当前这个Shader所在的模型上的所有顶点在屏幕上的坐标。它提供四种获取方式:
我们这里使用的是Raw模式,它提供了一个四维矢量的返回值。分别是XYZW,XYZ是这个坐标在屏幕空间上的坐标,W则是深度。Raw返回的数是一个没有将整体除以W的坐标,他就是他获得的是一个通过屏幕向水面做投射的效果,也就符合了我们的需求了。
我们新建一个Scene Depth节点,一个Screen Postion节点,和一个Subtract(减去)节点,将两个节点相减就可以了。要注意减法是不满足交换律的,必须是Scene Depth减Screen Postion。
但是你会发现,Screen Position返回的是一个四维矢量,包含XYZW。而Scene Depth是一个一维矢量,只包含W。我们不需要Screen Postion的XYZ,怎么把他们去掉呢?
我们新建一个Split节点,就可以将Screen Position的四个值分开了。但是我们发现,Split之后,它把我们的输出值变成了RGBA,而不是XYZW。RGBA是颜色的四维矢量,而不是坐标。但其实这里并不影响,对于引擎来说,XYZW和RGBA都是四维矢量,都是一样的。所以我们这里直接获取A就行了。
好的,现在我们获得了场景深度了。我们还差将相减出来的数值限定到0和1之间。但是在那之前,我希望我可以定义一下,对于这个Shader来说,多深的深度是显示深水颜色。因为我们现在如果直接将数值限定到0和1之间,0米就是浅水颜色,1米就是深水颜色了。如果我希望把10米设定为深水颜色,我应该怎么办呢?
其实我可以直接把获取的深度除以10就行了,如果我们希望让这个深度可以调节,我们可以在Properties中定义一下要除以的值。然后我们新建一个Divide(除以)节点,并和我们新建的Property以及Subtract的输出值相连,注意顺序。
前面我忘记把Scene Depth调整成Eye,以及Screen Position调整成Raw了。这里我们调整一下。
然后,我们新建一个Saturate节点,Saturate就相当于Clamp,他会把输入值限定到0和1之间,接着我们把Saturate输出的值连接到我们的Out上。我们点开Output节点的设置,把输出值类型调整成Vector1,也就是一维矢量。
我们也可以给输出的值改个名字。我这里就改成了Output。
点击左上角的Save Asset保存一下,然后我们回到我们的水面的Shader Graph上。
这个时候,我们搜索我们Sub Graph的名字,就可以找到,我们多了一个自定义的节点,可以把它添加进来。
可以看到,它有一个输入的Distance和一个输出Output,这个Distance就是我们在Sub Graph的Properties中定义的Property。我们可以再在水的Shader的Properties中定一个Property,用来控制这个Depth Fade Tu节点的Distance。
接着我们把Depth Fade Tu的输出作为Lerp的T,也就是作为决定两个颜色的过渡程度的值。
最后,我们可以全选我们目前创建的节点,右键选择 Group Selection。
这样我们就可以把所有的这些节点框起来,给他们打成一个组,并给组取一个名字。我这里取名叫Ripple and Deep。这样方便后面管理。
之所以这一期讲泡沫效果,是因为泡沫效果实际上也是利用深度来做的。
到了泡沫这一块,我们就可以用上之前被我们抛弃的另外两个噪点图的其中之一了。这里我们使用Gradient Noise,因为它比Simple Noise更加的层次分明一些,Simple Noise更加像是给现实风格使用的。
我们在Ripple and Deep的组外来创建一个Gradient Noise。
现在,我们希望这个Noise在接近物体边缘的地方产生,而不接近物体的地方就不要有泡沫效果,怎么办呢?我们还是可以获取深度。
如下图所示,我们在水中放一个方块。假设我们设定成,当我们的水面的深度,小于等于图中A的长度的时候,我们就在其表面上显示Noise;如果大于,就不显示,比如深度为B的时候。这样,水上C的区域就会出现Gradient Noise了。
那我们再次请出我们之前制作的Depth Fade Tu,以此来获取深度信息。
我们似乎没有办法直接裁剪Gradient Noise。如果有一个节点,可以让大于等于某一个深度的时候就自动剔除,当小于某一个深度的时候就保持显示就好了。诶,你有没有想起来,我们之前在波光效果里使用的一个节点,好像有类似的功能哦。
我们把Depth Fade作为我们的Edge,把Noise作为我们的输入。
这样我们可以形成一个特别好的过渡效果。假如我们Depth Fade里面的设置是,深度10米的时候输出1,深度0米的时候输出0,那么深度0米的时候,我就会得到一个大大的泡沫,而深度越接近10米,泡沫就越小。因为泡沫从中心到四周正好就是从黑色的0渐渐过渡到1的过程。泡沫在视觉上就会形成离物体越远越小,越近越大的效果。
新建一个泡沫颜色的Property,然后我要把这个颜色和水面原本的颜色合在一起,并且让他只在该显示泡沫的地方显示出来。这里我需要再一次用到Lerp了,A是水的原本颜色,B是泡沫颜色,而T是我们Step出来的值。
不过,如果现在我们把泡沫颜色的Alpha降低或者增高是没有用的,我们得让其同样的做用于刚刚我们Lerp的T上。
我们可以把Step出来的数值,和泡沫颜色的Alpha相乘,就可以实现了。节点如下图。
把泡沫颜色的Alpha用Split分出来,然后用Multiply让二者相乘,再把称完的值放到Lerp的T上。
第一个问题,我们把Noise的Scale增大,可以看到在不接近物体边缘的地方也有泡沫,而且不论水面离地面得多远都有。这是因为Step里,大于 等于 Edge的输入都会输出1,而当我们的深度到达或超过限定的深度的时候,DepthFade会返回1。而此时,纯白色,也就是Noise里的1,因为等于DepthFade给的1,所以二者相等,在经过Step的时候,Step就会把这些纯白色的部分也设置为1。
解决这个问题很简单,把Depth Fade的结果乘一下就行了。
这里我们将乘数放到Properties中,这样我们不仅可以避免水面上有很多没必要的白点,还可以调节泡沫的区域大小。
第二个问题,就是我们的泡沫不会动。因为移动的节点也不少,而且之后还有其他特效要用到,我们专门做一个Sub Graph来处理移动吧。新建Sub Graph并命名Movement Tu。
因为我们的Movement主要都是控制各个噪点图的UV输入,所以我们将Movement的输出节点的设置改为Vector2,二维矢量,并换个名字。
我们新建一个Tiling and Offset节点,这是最常用的对于UV进行操作的节点。它带有UV,Tilling,Offset 三个输入。UV就是使用物体的那一层UV,默认是UV 0,Tiling控制放大和缩小UV,Offset控制UV的偏移。我们加上一个Time节点来控制Offset,这样UV就能随着时间移动起来了。
然后,为了让移动的速度和UV的大小调节都变得可控,我们可以新建两个Properties来托管。并把Time和控制时间的Property相乘。
这里我先把Speed除以了10,然后再乘上时间,是因为我知道直接用Time来控制UV移动,会让其动起来特别快。为了避免要在材质面板里填写太多小数,我这里就先把Speed除了一下。你也可以不做这部操作而选择在材质面板填写小数。
这样,我们的Movement就做完了,把他放到我们原本的水的Shader的Gradient Noise的UV上。并添加Properties托管他的Speed和Scale。
感谢各位的观看,各位可以在 Twitter @shenkspz_peng 上关注一下我。时不时更新一些好康的东西。比如最近在做的二次元妹子角色。之后会更新一些自己开发的项目的进度报告之类的。
评论区
共 18 条评论热门最新