被甩后一个人打车看着窗外下大雨之雨滴玻璃shader【P1】
图文教程技术文章技术文库3D建模
显示全部 9
201 3
实名

通过了实名认证的内容创造者

发布于 2024-9-6 19:12:18

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 雾切酱 于 2019-4-17 11:51 编辑

https://zhuanlan.zhihu.com/p/60884288 黄小明

TitleP1.png

这是一个老外制作的一个水滴在玻璃上流淌的shader,效果非常好

所以偷偷学了过来,因为内容有点长我分为两个部分

我会尽可能详细解释制作思路和部分函数

(都是为了你,开心吗?)



-------------------------


shader效果

首先我们先仔细看下效果,要大概有一个基础的思路,不去考虑细节,最简单的基础的去解释下这幅画面,是不是雨滴在做一个向下的运动


思路图

那么接下来开始我们从第一步,做一个水滴,让他在UV上运动,然后我们再一步步加效果

-------------------------

首先我们来做一些准备工作

一双勤劳的手

一张图片

一颗刚刚被甩的心(这个很重要,心境没到位,怎么能做的好?)

不BB了,我们开始,准备工作自己想办法!!!!!


来我们先打开Unity新建一个UnlitShader,把里面关于FOG的先删除掉,我们先不考虑FOG

首先我们需要使用到两套UV,然后gv是我们用来画水滴的,我们想要更窄一些

  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float4 col = 0;

  4.                                 float2 aspect = float2(2, 1);
  5.                                 
  6.                                 float2 uv = i.uv*_Size*aspect;

  7.                                 float2 gv = frac(uv)-0.5;

  8.                                 col.rg = gv;

  9.                                 return col;
  10.                         }
点击此处复制文本



Frac函数的定义——可以简单理解为去除整数只保留小数


目前得到的结果

接下来我们要在每个格子中间画圆(水滴),做到这一步就需要用到length函数,因为我们需要控制大小所以我们还要用到smoothstep函数,然后我们注意一点,因为我们在前面给uv乘了aspect,所以我们需要在gv除以aspect这样我们才能得到一个正圆而不是一个椭圆。

然后我们为了方便查看,我们加上一段,对比gv的x和y返回一个(1,0,0,0)也就是红色

这个很好理解我们之前给gv-0.5,所以现在gv是(-0.5.0.5)最大的值只有0.5。

  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float4 col = 0;

  4.                                 float2 aspect = float2(2, 1);

  5.                                 float2 uv = i.uv*_Size*aspect;
  6.                                 float2 gv = frac(uv)-0.5;

  7.                         //        注意给gv除以aspect,否则会得到一个椭圆
  8.                                 float drop = smoothstep(0.05,0.03,length(gv/aspect));

  9.                                 col += drop;
  10.                         //        col.rg = gv;
  11.                                 
  12.                         //  为了观察方便制作线条
  13.                                 if (gv.x > 0.48 || gv.y > 0.49) {
  14.                                         return float4 (1, 0, 0, 0);
  15.                                 }
  16.                                 return col;
  17.                         }
点击此处复制文本



目前得到的结果

我们现在就需要让这个点动起来了,首先我们需要用到Time这个是Unity给的,我们直接用就好了,然后我们用sin函数做循环,但是单纯的sin满足不了我们的要求,我们需要一个快速下落缓慢回来的动画,所以我们可以去找一条满足的条件的函数曲线(推荐网站:

https://www.desmos.com/calculator

非常好用,自动生成曲线,只需要输入数值,不懂就瞎乘嘛只要能得到想要曲线就好)


-sin(x+sin(x+sin(x)0.5))

  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float t = _Time.y;
  4.                                 float4 col = 0;

  5.                                 float2 aspect = float2(2, 1);
  6.         
  7.                                 float2 uv = i.uv*_Size*aspect;
  8.                                 float2 gv = frac(uv)-0.5;

  9.                                 float x = 0;
  10.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
  11.                         
  12.                         //  控制gv.y来改变圆的位置
  13.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  14.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  15.                                 col += drop;
  16.                         //        col.rg = gv;
  17.                                 
  18.                         //  为了观察方便制作线条方便理解
  19.                                 if (gv.x > 0.48 || gv.y > 0.49) {

  20.                                         return float4 (1, 0, 0, 0);
  21.                                 }

  22.                                 return col;
  23.                         }
点击此处复制文本



目前得到的动画

现在我们已经有了一个水滴,我们添加一个拖尾(复制drop就可以了)
  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float t = _Time.y;
  4.                                 float4 col = 0;

  5.                                 float2 aspect = float2(2, 1);

  6.                         
  7.                                 float2 uv = i.uv*_Size*aspect;
  8.                                 float2 gv = frac(uv)-0.5;

  9.                                 float x = 0;
  10.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
  11.                         
  12.                         //  控制gv.y来改变圆的位置
  13.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  14.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  15.                
  16.                                 float2 dropTrailPos = (gv - float2(x, 0)) / aspect;
  17.                         //      这里我们希望生成多个水珠拖尾所以我们把dropTrailPos.y乘8然后frac
  18.                         //        还是一样因为之前给dropTrailPos.y乘于了8所以我们需要除以8
  19.                         //        然后-0.03是因为我们得到的是一个半圆
  20.                                 dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
  21.                                 float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));

  22.                                 col += drop;
  23.                                 col += dropTrail;

  24.                         //  为了观察方便制作线条方便理解
  25.                                 if (gv.x > 0.48 || gv.y > 0.49) {

  26.                                         return float4 (1, 0, 0, 0);
  27.                                 }

  28.                                 return col;
  29.                         }
点击此处复制文本



目前得到的结果

现在我们需要做一个水珠的渐变,然后让uv也动起来,这样就可以做到循环运动

  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float t = _Time.y;
  4.                                 
  5.                             float4 col = 0;


  6.                                 float2 aspect = float2(2, 1);
  7.                                 float2 uv = i.uv*_Size*aspect;
  8.                         //  控制uv运动配合水滴下落
  9.                                 uv.y += t * 0.25;
  10.                                 float2 gv = frac(uv)-0.5;

  11.                                 float x = 0;
  12.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
  13.                         
  14.                         //  控制gv.y来改变圆的位置
  15.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  16.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  17.                
  18.                                 float2 dropTrailPos = (gv - float2(x, t * 0.25)) / aspect;
  19.                         //  这里我们为了生成多个水珠拖尾所以我们把dropTrailPos.y 乘8然后frac
  20.                         //        还是一样因为之前给dropTrailPos.y 乘于了8所以我们需要除以8
  21.                         //        然后-0.03是因为我们得到的是一个半圆
  22.                                 dropTrailPos.y = (frac(dropTrailPos.y  * 8)/8)-0.03;
  23.                                 float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
  24.                         
  25.                         //  y值在-0.45与0.45之间,然后我们的gv.v在0.5,dropPos.y=gv.y-y,建议输出结果查看下就明白了
  26.                                 dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
  27.                                 dropTrail *= smoothstep(0.5, y, gv.y);

  28.                                 col += drop;
  29.                                 col += dropTrail;

  30.                         //  为了观察方便制作线条方便理解
  31.                                 if (gv.x > 0.48 || gv.y > 0.49) {

  32.                                         return float4 (1, 0, 0, 0);
  33.                                 }

  34.                                 return col;
  35.                         }
点击此处复制文本




之前我们用sin控制了水滴的y方向运动,现在我们现在给水滴加上x的运动

x方向的运动尽可能要多一些波动,更像是水滴的运动


sin(3x)sin(x)^6

  1. fixed4 frag (v2f i) : SV_Target
  2.                         {        
  3.                                 float t = _Time.y;

  4.                                 float4 col = 0;

  5.                                 float2 aspect = float2(2, 1);
  6.                                 float2 uv = i.uv*_Size*aspect;
  7.                         //  控制uv运动配合水滴下落
  8.                                 uv.y += t * 0.25;
  9.                                 float2 gv = frac(uv)-0.5;
  10.                         
  11.                         //  用这个w值来控制拉伸程度        
  12.                                 float w = i.uv.y *10;
  13.                         //  现在我们不能用t来控制了,因为我们需要雨滴进行拉伸,所以我们利用uv来控制
  14.                                 float x = sin(3*w)*pow(sin(w),6)*0.45;
  15.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
  16.                         //  改变了水滴的形状,目前是gv.x(-0.5,0.5)相乘得到一个对称的值
  17.                                 y -= (gv.x-x)*(gv.x-x);
  18.                         
  19.                         //  控制gv.y来改变圆的位置
  20.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  21.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  22.                                 float2 dropTrailPos = (gv - float2(x,t * 0.25)) / aspect;
  23.                         //  这里我们为了生成多个水珠拖尾所以我们把dropTrailPos.y乘8然后frac
  24.                         //        还是一样因为之前给dropTrailPos.y乘于了8所以我们需要除以8
  25.                         //        然后-0.03是因为我们得到的是一个半圆
  26.                                 dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
  27.                                 float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
  28.                         
  29.                         //  y值在-0.45与0.45之间,然后我们的gv.v在0.5,dropPos.y=gv.y-y,建议输出结果查看下就明白了
  30.                                 dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
  31.                                 dropTrail *= smoothstep(0.5, y, gv.y);


  32.                                 col += drop;
  33.                                 col += dropTrail;
  34.                                 
  35.                         //  为了观察方便制作线条方便理解
  36.                                 if (gv.x > 0.48 || gv.y > 0.49) {

  37.                                         return float4 (1, 0, 0, 0);
  38.                                 }

  39.                                 return col;
  40. }
点击此处复制文本




现在我们加入noise,让雨滴随机运动。

  1. //noise函数块
  2.                         float N21(float2 p){
  3.                                 p = frac(p*float2(123.34,345.45));
  4.                                 p +=dot( p,p+34.345 ) ;
  5.                                 return frac(p.x*p.y);
  6. }

  7.                         fixed4 frag (v2f i) : SV_Target
  8.                         {        
  9.                                 float t = _Time.y;
  10.                                 
  11.                                 float4 col = 0;

  12.                                 float2 aspect = float2(2, 1);
  13.                                 float2 uv = i.uv*_Size*aspect;
  14.                         //  控制uv运动配合水滴下落
  15.                                 uv.y += t * 0.25;
  16.                                 float2 gv = frac(uv)-0.5;
  17.                         //        id用来得到每格水滴
  18.                                 float2 id = floor(uv);
  19.                         
  20.                         //  得到每一格0或者1的随机值
  21.                                 float n =N21(id);
  22.                         //  用t+上现在每格水滴的系数,达到每格的时间都不一样
  23.                                 t+= n*5.345;

  24.                         //  用这个w值来控制拉伸程度        
  25.                                 float w = i.uv.y *10;

  26.                         //  我们把x也做一个随机的处理,使用n,得到-0.4,0.4的值
  27.                                 float x = (n-0.5)*0.8;
  28.                         //  0.4-abs( x )的值是控制幅度,然后乘以之前我们做的运动曲线
  29.                                 x += (0.4-abs(x))*sin(3*w)*pow(sin(w),6)*0.45;
  30.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;
  31.                         //  改变了水滴的形状,目前是gv.x(-0.5,0.5)相乘得到一个对称的值
  32.                                 y -= (gv.x-x)*(gv.x-x);
  33.                         

  34.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  35.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  36.                                 float2 dropTrailPos = (gv - float2(x, t*0.25)) / aspect;
  37.                                 dropTrailPos.y = (frac(dropTrailPos.y * 8)/8)-0.03;
  38.                                 float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
  39.                         
  40.                                 dropTrail *= smoothstep(-0.05, 0.05, dropPos.y);
  41.                                 dropTrail *= smoothstep(0.5, y, gv.y);


  42.                                 col += drop;
  43.                                 col += dropTrail;

  44.                         //  为了观察方便制作线条方便理解
  45.                                 if (gv.x > 0.48 || gv.y > 0.49) {

  46.                                         return float4 (1, 0, 0, 0);
  47.                                 }
  48.                                 return col;
  49.              }
点击此处复制文本




现在我们可以做最后一步了,在现在的效果下,添加一个直线的拖尾,然后加上图片,用目前的drop+dropTrail改变图片的UV就可以达到水滴折射的效果了


实在没办法,我只能拿我老婆的照片来用

  1. Shader "Billy/RainGlass_OP"
  2. {
  3.         Properties
  4.         {
  5.                 _MainTex ("Texture", 2D) = "white" {}
  6.                 _Size("Size",Float)= 1
  7.                 _Distortion("Distortion",range(0,1))=1
  8.         }
  9.         SubShader
  10.         {
  11.                 Tags { "RenderType"="Opaque" }
  12.                 LOD 100

  13.                 Pass
  14.                 {
  15.                         CGPROGRAM
  16.                         #pragma vertex vert
  17.                         #pragma fragment frag
  18.                         #include "UnityCG.cginc"

  19.                         struct appdata
  20.                         {
  21.                                 float4 vertex : POSITION;
  22.                                 float2 uv : TEXCOORD0;
  23.                         };

  24.                         struct v2f
  25.                         {
  26.                                 float2 uv : TEXCOORD0;
  27.                                 UNITY_FOG_COORDS(1)
  28.                                 float4 vertex : SV_POSITION;
  29.                         };

  30.                         sampler2D _MainTex;
  31.                         float4 _MainTex_ST;
  32.                         float _Size;
  33.             float _Distortion;
  34.                         
  35.                         v2f vert (appdata v)
  36.                         {
  37.                                 v2f o;
  38.                                 o.vertex = UnityObjectToClipPos(v.vertex);
  39.                                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
  40.                                 return o;
  41.                         }
  42.                         

  43.                         float N21(float2 p){
  44.                                 p = frac(p*float2(123.34,345.45));
  45.                                 p +=dot( p,p+34.345 ) ;
  46.                                 return frac(p.x*p.y);
  47. }

  48.                         fixed4 frag (v2f i) : SV_Target
  49.                         {        
  50.                                 float t = fmod(_Time.y,7200);
  51.                                 float4 col =0;
  52.                                 float2 aspect = float2(2, 1);
  53.                                 float2 uv = i.uv*_Size*aspect;

  54.                                 uv.y += t * 0.25;
  55.                                 float2 gv = frac(uv)-0.5;
  56.                                 float2 id = floor(uv);
  57.                         

  58.                                 float n =N21(id);

  59.                                 t+= n*6.28631;


  60.                                 float w = i.uv.y *10;

  61.                                 float x = (n-0.5)*0.8;

  62.                                 x += (0.4-abs(x))*sin(3*w)*pow(sin(w),6)*0.45;
  63.                                 float y =-sin(t+sin(t+sin(t)*0.5))*0.45;

  64.                                 y -= (gv.x-x)*(gv.x-x);
  65.                         

  66.                                 float2 dropPos =(gv - float2(x, y))/aspect;
  67.                                 float drop = smoothstep(0.05,0.03,length(dropPos));

  68.                
  69.                                 float2 dropTrailPos = (gv - float2(x, t*0.25)) / aspect;
  70.                                 dropTrailPos.y = (frac(dropTrailPos.y* 8)/8)-0.03;
  71.                                 float dropTrail = smoothstep(0.03, 0.02, length(dropTrailPos));
  72.                                 float fogTrail = smoothstep(-0.05, 0.05, dropPos.y);
  73.                                 fogTrail *= smoothstep(0.5, y, gv.y);
  74.                                 dropTrail *= fogTrail;

  75.                                 fogTrail *= smoothstep(0.05,0.04,abs(dropPos.x));

  76.                                 col += fogTrail*0.5;
  77.                                 col += dropTrail;
  78.                                 col += drop;

  79.                                 
  80.                                 float2 offs = drop*dropPos+dropTrail*dropTrailPos;
  81.                                 col = tex2D(_MainTex,i.uv+offs*_Distortion);
  82.                                 return col;
  83.                         }
  84.                         ENDCG
  85.                 }
  86.         }
  87. }
点击此处复制文本



其实写完,这一部分先写到这,其实写完发现可能用shaderforge解释会更加方便和直观,下一部分会,贴上shaderforge的制作流程,会添加多层水滴和模糊镜面效果,如果有没看懂的留意下部分,用shaderforge试试就懂了。

记住保持一颗失恋的心,你会做的更好。

加油,骚年!

码字真不容易........

评分

参与人数 3元素币 +66 活跃度 +14 贡献值 +1 展开 理由
诺一 + 8 厉害啊!
浮生如夢... + 6 很棒 支持一下!!!
小素素 + 66 + 1 666

查看全部评分

使用道具 <
小素素  发表于 2019-4-16 17:42:06  
2#
文中是你老婆吗?
回复 收起回复
使用道具
zy2346148  发表于 2019-4-17 08:00:52  
3#
谢谢您  很少能见到你这么负责的人了  谢谢
回复 收起回复
使用道具
雾切酱  发表于 2019-4-17 12:05:21  
4#
小薇薇 发表于 2019-4-16 17:42
文中是你老婆吗?

哈哈哈是呢;)
回复 收起回复
使用道具
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表