top of page
  • 作家相片Lingheng Tao

Unity Shader #7 Raindrops on Lens

已更新:2023年10月31日


本篇主要写一下用URP自带的ShaderGraph实现在屏幕上显示水珠流水的效果。其中还包括一些Shader中常用的技巧。我们的整体思路可以分为画出雨滴、画出拖尾以及模糊其它部分。


UV 的理解


首先,在 Shader Graph 中,如果我们使用 UV 节点直接获取 UV 的话,会看到这么一个预览图:

用 Swizzle 节点配合 Mask,我们先看 x,y,z,w 四个值的分量。

在预览中,我们知道 0 是越接近黑色的颜色,1是越接近白色的颜色。使用齐次坐标的时候,我们知道点的 w 分量都是 1, 所以第四张贴图的颜色是纯白的。UV 坐标本身是 2D 坐标,也因此 z 都是 0。重点观察 x, y 分量,也就是所谓的 u 和 v。x 从左到右从 0 到 1,y 从下到上从 0 到 1。


使用 Mask 取出x,y 两个值的时候,我们就能看到这种红绿彩色图了。


画出雨滴


第 1 步 画圆


画出雨滴说白了就是画个圆,然后在圆形里面显示周围像素的折射。

所以首先第一步是要画个圆。我们很容易想到,圆的一个属性就是离中心的距离一致,所以肯定有什么与中心和距离两个关键词有关。很简单,就是 UV 上的某个点和原点的距离——计算这个就直接用点乘求平方根即可。

dot product + square root 的效果与节点 Length 是完全一致的。从上图可以看出来,确实,距离原点越近也就越黑。我们希望让这个过度的边缘变的更锐利一些。但是显然,我们不需要和 (0,0) 这个点求距离,如果希望类似于圆的形状出现,那么显然使用与(0.5, 0.5) 这个点的距离更合适。

下面就是让这个圆变的更加清晰。我们希望它在最大值、最小值之间有一个平滑过度,并且限制最大值最小值的范围极其接近,这样的话就可以使得边缘变得比较清晰。有一个节点 Smoothstep 就完全符合我们的要求。


让输入值接入 In, 然后将 distance 的输出映射到 0.11 到 0.1 之间,就可以看出一个很清晰的圆了。


第 2 步 画很多圆


雨滴画一个圆肯定不够,我们现在希望画很多圆。设想,如果我们的 UV 都是 [0,1] 的范围,我们想在这里分出几份。我们先试试看比如直接乘这个份数。

我们分析一下,这里 RainSize 变量已经设置成 4,那么现在 UV 范围就从 [0,1] 变成了 [0,4]。超过1的部分都会被显示为黄色,所以现在看到的 Multiply 的结果已经有大片的黄色了。

那,有没有什么办法,让每个长度为 1 的单位区间内的值都能显示 [0,1] 的颜色呢?其实当然是有的,我们只要无视整数部分,只看小数部分就行。用 Fraction 节点即可做到这件事。

Very nice, 我们已经看到了复制出多份的 UV 颜色了,这个时候取与 (0.5, 0.5) 的distance,当然就能画出多个圆了。

雨滴圆不能太大,我们调整一下圆的大小,只需要调整 Smoothstep 两侧的边缘就可以了。


[OPTIONAL] 第 2.5 步 画出每个圆所在的 UV 边界


为了方便之后观察,我们可以把每个圆所在的 UV 边界画一下。这个部分可以先自己思考一下,然后在对照下面的连连看做一下。

很简单,就还是用乘 RainSize 之后的 uv 配合 smoothstep把边界推出来就行,最后乘上个颜色,加到原来的圆点图上就行。


第 3 步 将正方形区域变成长方形区域


真正的雨滴一般来说是在竖直方向上动的比较快,水平方向上动的比较慢,所以可能最好是用长方形,而不是正方形。这一步也不难。


把前面的 RainSize 从一个 float 改成一个二维向量就可以啦。但是,又出现了新的问题。

这个圆变成椭圆了。这个也简单,我们再把这个值除回去就行。

注意:因为我们除以了2,现在 x 的 UV 范围就变成了 [0, 0.5],因此,在计算 distance 的时候我们不能在比较与 (0.5, 0.5) 的距离,而应该是 (0.25, 0.5) 的距离了。


第 4 步 移动圆形


下面就要让这个圆形随时间动起来。首先先用能获得时间的节点 Time。简单来说,我们让 uv 的 y 值不断随着时间变化就行,把 x,y 值分离出来,然后单独去让 y 值加上 Time 的输出,再组合旧的 x 与新的 y 即可。

这一部分结束之后,圆形就应该在竖直方向上移动了。但是现在的移动太均匀,我们稍微给一点快慢变化。我们希望有周期性变化,所以首先先想到 sin 函数,我们让 y 值减去一个随时间变化的 sin 值。

这样的连线做完之后,你可能会看到一个在自己的框里面动得很怪的一个圆。它时不时会回到自己的框里,时不时会跑出去。速度确实不那么均匀了,但是超出框和过于规律的运动也依然不是我们想要的。

先给 sin 乘上一个 0.3 来确保圆在范围内移动。然后接下来让我们研究一下移动周期的问题。


随便找个图象计算器。这是 y = sin x 的图像。我们希望让周期移动的轴往一边偏移,然后整体保持一个比较明显的周期性。

观察一下 y = sin(x + sin(x + 0.5 sin(x))) 的函数图像。

基本上满足我们的要求,所以我们就用这个函数就好了。


新开一个 SubGraph,我们在这里面输出这个函数的结果。

然后在原来的 SubGraph 中使用这个函数。


画出拖尾


雨滴那个圆形在上面的步骤已经基本做的差不多了。下面就要做拖尾的内容了。


首先,我们在 x 方向上,并不需要有额外的操作。我们先假设拖尾就是多个小的圆形水珠跟在大水珠的屁股后面。所以我们要在 y 方向上额外划出新的 uv 范围,就跟之前画长方形范围一样。简单 Multiply 一个数值就行。

还是类似地,与 uv 中心求距离,加一步 smoothstep 即可(在这里我们用先偏移 UV 再求 Length 的方法,Length 可以理解为与原点的距离)。与之前一样,不要忘记把乘上的 scale 在组 UV 时除回去,否则出来的雨滴可能就是椭圆形了。

现在,雨滴下面也有拖尾。我们要把雨滴下面的拖尾给隐藏掉。

有没有哪个节点现在返回了大圆的 y 值?有的,我们上面的三次 sin 输出的值就是大圆的 y 值。那我们在这个 y 值上做一次 smoothstep,让下半显示为黑色,上半显示为白色,然后乘以一下所有的小圆就行了。

现在看到的效果就是大圆向下就没有小圆了。

再给上面输出的不随时间变化的 y 做一个渐变图,然后乘上现有的圆以完成渐隐效果。


X 方向上的摆动


在 X 方向上,我们也用 sin 函数实现摆动,实现的结果需要在 x 方向上的 [0,1] 上动,所以我们用 Subtract 来处理。注意摆动范围不能超出边框。(用第二个 Multiply 控制 Sin 的输出值范围)



然后去稍微调整一下之前设置过的 sin 复合函数上的scale, 以确保shader如逾期效果运行。

现在 X 上的摆动与 Y 方向上的速度也没有关系,也是我们需要修复的部分。


首先,X 上的摆动不应该再受到时间的影响,所以我们应该先让 sin 前与 Time 无关,用上面的函数曲线分析,可以找出一个类似的与 sin(y) 有关的函数,我们这里不予赘述,直接给出一个可行的连法(实现速度快慢相似的曲线当然也可以使用)。

现在拖尾会跟着这个曲线一起动。这也是不太对的,我们要修复这个问题。

我们把它们与时间相减进行相对静止即可。


绘制路径


绘制滑过的路径需要分成三个部分。我们需要一个以雨滴为分界的上白下黑的图,需要另一个路径为白两侧为黑的图,还有一个从雨滴下方到 uv 区域顶部的渐变图。


雨滴分界上白下黑

这个很简单了,用 Smoothstep 就可以立刻完成了。

edge1 和 edge2 设置值分别为-0.02与0。


路径为白两侧为黑

拿 X 坐标来做两侧的分界,但是要记得对 X 做一次绝对值。

把以上两张图相乘,就会有只有雨滴走过的部分才有痕迹的图了。

渐变图

找到之前做过的一个渐变图直接用就好了。

现在应该有这样的效果了。


28 次查看1 則留言

最新文章

查看全部

Unity Engine #5 Physics

#GameEngine #UnityEngine #GameProgramming 这篇笔记主要写一下关于碰撞检测和刚体(Rigidbody)组件的一些特性。

1 Comment


袭 白
袭 白
Oct 31, 2023

taolaoshiyyds

Like
bottom of page