您需要 登录 才可以下载或查看,没有账号?注册
x
本帖最后由 雾切酱 于 2019-4-17 11:51 编辑
转https://zhuanlan.zhihu.com/p/60884288 黄小明
这是一个老外制作的一个水滴在玻璃上流淌的shader,效果非常好
所以偷偷学了过来,因为内容有点长我分为两个部分
我会尽可能详细解释制作思路和部分函数
(都是为了你,开心吗?)
-------------------------
shader效果
首先我们先仔细看下效果,要大概有一个基础的思路,不去考虑细节,最简单的基础的去解释下这幅画面,是不是雨滴在做一个向下的运动
思路图
那么接下来开始我们从第一步,做一个水滴,让他在UV上运动,然后我们再一步步加效果
-------------------------
首先我们来做一些准备工作
一双勤劳的手
一张图片
一颗刚刚被甩的心(这个很重要,心境没到位,怎么能做的好?)
不BB了,我们开始,准备工作自己想办法!!!!!
来我们先打开Unity新建一个UnlitShader,把里面关于FOG的先删除掉,我们先不考虑FOG
首先我们需要使用到两套UV,然后gv是我们用来画水滴的,我们想要更窄一些
- fixed4 frag (v2f i) : SV_Target
- {
- float4 col = 0;
- float2 aspect = float2(2, 1);
-
- float2 uv = i.uv*_Size*aspect;
- float2 gv = frac(uv)-0.5;
- col.rg = gv;
- return col;
- }
点击此处复制文本
Frac函数的定义——可以简单理解为去除整数只保留小数
目前得到的结果
接下来我们要在每个格子中间画圆(水滴),做到这一步就需要用到length函数,因为我们需要控制大小所以我们还要用到smoothstep函数,然后我们注意一点,因为我们在前面给uv乘了aspect,所以我们需要在gv除以aspect这样我们才能得到一个正圆而不是一个椭圆。
然后我们为了方便查看,我们加上一段,对比gv的x和y返回一个(1,0,0,0)也就是红色
这个很好理解我们之前给gv-0.5,所以现在gv是(-0.5.0.5)最大的值只有0.5。
- fixed4 frag (v2f i) : SV_Target
- {
- float4 col = 0;
- float2 aspect = float2(2, 1);
- float2 uv = i.uv*_Size*aspect;
- float2 gv = frac(uv)-0.5;
- // 注意给gv除以aspect,否则会得到一个椭圆
- float drop = smoothstep(0.05,0.03,length(gv/aspect));
- col += drop;
- // col.rg = gv;
-
- // 为了观察方便制作线条
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
目前得到的结果
我们现在就需要让这个点动起来了,首先我们需要用到Time这个是Unity给的,我们直接用就好了,然后我们用sin函数做循环,但是单纯的sin满足不了我们的要求,我们需要一个快速下落缓慢回来的动画,所以我们可以去找一条满足的条件的函数曲线(推荐网站:
https://www.desmos.com/calculator
非常好用,自动生成曲线,只需要输入数值,不懂就瞎乘嘛只要能得到想要曲线就好)
-sin(x+sin(x+sin(x)0.5))
- fixed4 frag (v2f i) : SV_Target
- {
- float t = _Time.y;
- float4 col = 0;
- float2 aspect = float2(2, 1);
-
- float2 uv = i.uv*_Size*aspect;
- float2 gv = frac(uv)-0.5;
- float x = 0;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
-
- // 控制gv.y来改变圆的位置
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
- col += drop;
- // col.rg = gv;
-
- // 为了观察方便制作线条方便理解
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
目前得到的动画
现在我们已经有了一个水滴,我们添加一个拖尾(复制drop就可以了)
- fixed4 frag (v2f i) : SV_Target
- {
- float t = _Time.y;
- float4 col = 0;
- float2 aspect = float2(2, 1);
-
- float2 uv = i.uv*_Size*aspect;
- float2 gv = frac(uv)-0.5;
- float x = 0;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
-
- // 控制gv.y来改变圆的位置
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
-
- float2 dropTrailPos = (gv - float2(x, 0)) / aspect;
- // 这里我们希望生成多个水珠拖尾所以我们把dropTrailPos.y乘8然后frac
- // 还是一样因为之前给dropTrailPos.y乘于了8所以我们需要除以8
- // 然后-0.03是因为我们得到的是一个半圆
- dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
- float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
- col += drop;
- col += dropTrail;
- // 为了观察方便制作线条方便理解
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
目前得到的结果
现在我们需要做一个水珠的渐变,然后让uv也动起来,这样就可以做到循环运动
- fixed4 frag (v2f i) : SV_Target
- {
- float t = _Time.y;
-
- float4 col = 0;
- float2 aspect = float2(2, 1);
- float2 uv = i.uv*_Size*aspect;
- // 控制uv运动配合水滴下落
- uv.y += t * 0.25;
- float2 gv = frac(uv)-0.5;
- float x = 0;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
-
- // 控制gv.y来改变圆的位置
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
-
- float2 dropTrailPos = (gv - float2(x, t * 0.25)) / aspect;
- // 这里我们为了生成多个水珠拖尾所以我们把dropTrailPos.y 乘8然后frac
- // 还是一样因为之前给dropTrailPos.y 乘于了8所以我们需要除以8
- // 然后-0.03是因为我们得到的是一个半圆
- dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
- float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
-
- // y值在-0.45与0.45之间,然后我们的gv.v在0.5,dropPos.y=gv.y-y,建议输出结果查看下就明白了
- dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
- dropTrail *= smoothstep(0.5, y, gv.y);
- col += drop;
- col += dropTrail;
- // 为了观察方便制作线条方便理解
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
之前我们用sin控制了水滴的y方向运动,现在我们现在给水滴加上x的运动
x方向的运动尽可能要多一些波动,更像是水滴的运动
sin(3x)sin(x)^6
- fixed4 frag (v2f i) : SV_Target
- {
- float t = _Time.y;
- float4 col = 0;
- float2 aspect = float2(2, 1);
- float2 uv = i.uv*_Size*aspect;
- // 控制uv运动配合水滴下落
- uv.y += t * 0.25;
- float2 gv = frac(uv)-0.5;
-
- // 用这个w值来控制拉伸程度
- float w = i.uv.y *10;
- // 现在我们不能用t来控制了,因为我们需要雨滴进行拉伸,所以我们利用uv来控制
- float x = sin(3*w)*pow(sin(w),6)*0.45;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
- // 改变了水滴的形状,目前是gv.x(-0.5,0.5)相乘得到一个对称的值
- y -= (gv.x-x)*(gv.x-x);
-
- // 控制gv.y来改变圆的位置
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
- float2 dropTrailPos = (gv - float2(x,t * 0.25)) / aspect;
- // 这里我们为了生成多个水珠拖尾所以我们把dropTrailPos.y乘8然后frac
- // 还是一样因为之前给dropTrailPos.y乘于了8所以我们需要除以8
- // 然后-0.03是因为我们得到的是一个半圆
- dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
- float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
-
- // y值在-0.45与0.45之间,然后我们的gv.v在0.5,dropPos.y=gv.y-y,建议输出结果查看下就明白了
- dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
- dropTrail *= smoothstep(0.5, y, gv.y);
- col += drop;
- col += dropTrail;
-
- // 为了观察方便制作线条方便理解
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
现在我们加入noise,让雨滴随机运动。
- //noise函数块
- float N21(float2 p){
- p = frac(p*float2(123.34,345.45));
- p +=dot( p,p+34.345 ) ;
- return frac(p.x*p.y);
- }
- fixed4 frag (v2f i) : SV_Target
- {
- float t = _Time.y;
-
- float4 col = 0;
- float2 aspect = float2(2, 1);
- float2 uv = i.uv*_Size*aspect;
- // 控制uv运动配合水滴下落
- uv.y += t * 0.25;
- float2 gv = frac(uv)-0.5;
- // id用来得到每格水滴
- float2 id = floor(uv);
-
- // 得到每一格0或者1的随机值
- float n =N21(id);
- // 用t+上现在每格水滴的系数,达到每格的时间都不一样
- t+= n*5.345;
- // 用这个w值来控制拉伸程度
- float w = i.uv.y *10;
- // 我们把x也做一个随机的处理,使用n,得到-0.4,0.4的值
- float x = (n-0.5)*0.8;
- // 0.4-abs( x )的值是控制幅度,然后乘以之前我们做的运动曲线
- x += (0.4-abs(x))*sin(3*w)*pow(sin(w),6)*0.45;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
- // 改变了水滴的形状,目前是gv.x(-0.5,0.5)相乘得到一个对称的值
- y -= (gv.x-x)*(gv.x-x);
-
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
- float2 dropTrailPos = (gv - float2(x, t*0.25)) / aspect;
- dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
- float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
-
- dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
- dropTrail *= smoothstep(0.5, y, gv.y);
- col += drop;
- col += dropTrail;
- // 为了观察方便制作线条方便理解
- if (gv.x > 0.48 || gv.y > 0.49) {
- return float4 (1, 0, 0, 0);
- }
- return col;
- }
点击此处复制文本
现在我们可以做最后一步了,在现在的效果下,添加一个直线的拖尾,然后加上图片,用目前的drop+dropTrail改变图片的UV就可以达到水滴折射的效果了
实在没办法,我只能拿我老婆的照片来用
- Shader "Billy/RainGlass_OP"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- _Size("Size",Float)= 1
- _Distortion("Distortion",range(0,1))=1
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- struct v2f
- {
- float2 uv : TEXCOORD0;
- UNITY_FOG_COORDS(1)
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float _Size;
- float _Distortion;
-
- v2f vert (appdata v)
- {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
-
- float N21(float2 p){
- p = frac(p*float2(123.34,345.45));
- p +=dot( p,p+34.345 ) ;
- return frac(p.x*p.y);
- }
- fixed4 frag (v2f i) : SV_Target
- {
- float t = fmod(_Time.y,7200);
- float4 col =0;
- float2 aspect = float2(2, 1);
- float2 uv = i.uv*_Size*aspect;
- uv.y += t * 0.25;
- float2 gv = frac(uv)-0.5;
- float2 id = floor(uv);
-
- float n =N21(id);
- t+= n*6.28631;
- float w = i.uv.y *10;
- float x = (n-0.5)*0.8;
- x += (0.4-abs(x))*sin(3*w)*pow(sin(w),6)*0.45;
- float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
- y -= (gv.x-x)*(gv.x-x);
-
- float2 dropPos =(gv - float2(x, y))/aspect;
- float drop = smoothstep(0.05,0.03,length(dropPos));
-
- float2 dropTrailPos = (gv - float2(x, t*0.25)) / aspect;
- dropTrailPos.y = (frac(dropTrailPos.y* 8)/8)-0.03;
- float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
- float fogTrail = smoothstep(-0.05, 0.05, dropPos.y);
- fogTrail *= smoothstep(0.5, y, gv.y);
- dropTrail *= fogTrail;
- fogTrail *= smoothstep(0.05,0.04,abs(dropPos.x));
- col += fogTrail*0.5;
- col += dropTrail;
- col += drop;
-
- float2 offs = drop*dropPos+dropTrail*dropTrailPos;
- col = tex2D(_MainTex,i.uv+offs*_Distortion);
- return col;
- }
- ENDCG
- }
- }
- }
点击此处复制文本
其实写完,这一部分先写到这,其实写完发现可能用shaderforge解释会更加方便和直观,下一部分会,贴上shaderforge的制作流程,会添加多层水滴和模糊镜面效果,如果有没看懂的留意下部分,用shaderforge试试就懂了。
记住保持一颗失恋的心,你会做的更好。
加油,骚年!
码字真不容易........ |