蛮牛:最近群友反馈,希望多发一些Shader的内容和其他进阶的内容,所以...
本篇将图解每个步骤,让大家都可以看懂~
效果预览
现在我们先做涟漪部分
首先Shader 需要一个 材质时间 ,Frac 函数可以让我们得到这样一条曲线
float3 emissive = Frac((_Time *_speed));
我们先直接返回到屏幕可以看到这样的黑白渐变效果 return float4(emissive,1);
下面我们需要准备一张这样的图片(如何生成这张图片那就是在水一篇了,滑稽.jpg)
文章底部打包下载链接
用刚才的那张黑白渐变图 反向后 减去这张,会得到下图的效果
float3 emissive = 1-(Frac((_Time *_speed)));
float3 _Texture_var = tex2D(_Texture,TRANSFORM_TEX(i.uv0, _Texture)).rgb;
return float4(emissive - _Texture_var,1);
接着 使用距离函数 distance 来进一步计算得到圆环形状,原理如下
distance 和 0.05 比较
0.05 = 0 黑 0.05< 灰/白 0.05 > 灰/白
float3 emissive = 1-(Frac((_Time *_speed)));
float3 _Texture_var = tex2D(_Texture,TRANSFORM_TEX(i.uv0, _Texture)).rgb;
float maskColor = distance(emissive.r - _Texture_var.r,0.05);
return float4(maskColor,maskColor,maskColor,1);
然后我们会得到这样一张图
然后再让这张图除以 0.05
float3 emissive = 1-(Frac((_Time *_speed)));
float3 _Texture_var = tex2D(_Texture,TRANSFORM_TEX(i.uv0, _Texture)).rgb;
float maskColor = subtract(distance(emissive.r - _Texture_var.r,0.05)/0.05);
return float4(maskColor,maskColor,maskColor,1);
会得到下图效果,此时基础的涟漪Mask 就有了
通过偏移时间并对刚才过程如法炮制并偏移一点UV 将两张图Add到一起,混合的时候可以引入sin曲线 让两图图交替淡入
float3 emissive = 1-(Frac((_Time *_speed)));
float3 emissive2 = 1-(Frac((_Time *_speed)+ 0.5));//偏移一点时间,让涟漪扩散时间错开float3 _Texture_var = tex2D(_Texture,TRANSFORM_TEX(i.uv0, _Texture)).rgb;
//UV 也偏移一点,让两张图错开一些float3 _Texture_var2 = tex2D(_Texture,TRANSFORM_TEX(i.uv0 + float2(0.5,0.5), _Texture)).rgb;
float maskColor = saturate(1 - distance(emissive.r - _Texture_var.r,0.05)/0.05);
float maskColor2 = saturate(1 - distance(emissive2.r - _Texture_var2.r,0.05)/0.05);
float maskSwitch = saturate(abs(sin((_Time * 0.5))));//两张图交替淡入float finalColor = lerp(maskColor , maskColor2 ,maskSwitch );
return float4(finalColor ,finalColor ,finalColor ,1);
涟漪现在有了,我们开始制作 墙面/玻璃 流水的效果 ,首先流水的部分肯定是垂直于地面,我们可以用世界空间位置来投影
我们使用这张图 水痕图
以世界空间xy ,zy投影后 再使用 世界空间abs(normalDir)方向 来控制投影的范围 可以得到这样的效果
struct VertexInput {
float4 vertex : POSITION;
float2 texcoord0 : TEXCOORD0;
float3 normal : NORMAL;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float4 objectPos : TEXCOORD3;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.uv0 = v.texcoord0;
o.pos = UnityObjectToClipPos( v.vertex );
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.normalDir = normalize(UnityObjectToWorldNormal(v.normal));
o.objectPos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
return o;
}
float4 frag(VertexOutput i) : COLOR {
float2 verticalUv = float2(i.posWorld.x, i.posWorld.y) * _Tiling;
float2 verticalUv2 = float2(i.posWorld.z, i.posWorld.y) * _Tiling;
float4 Texture_var = tex2D(_Texture, TRANSFORM_TEX(verticalUv, _Texture) );
float4 Texture_var2 = tex2D(_Texture, TRANSFORM_TEX(verticalUv2, _Texture) );
float4 finalColor = lerp(Texture_var ,Texture_var2 , abs(i.normalDir.x));
return float4(finalColor);
}
这时我们移动球,贴图是在世界方向固定的
如果想跟随球体只需要 物体坐标 减去 世界坐标 即可
float2 verticalUv = float2(i.objectPos.x - i.posWorld.x, i.objectPos.y - i.posWorld.y) * _Intensity;
float2 verticalUv2 = float2(i.objectPos.z - i.posWorld.z, i.objectPos.y - i.posWorld.y) * _Intensity;
我们这时需要再增加一张Mask贴图
然后乘以刚才的水痕图 并控制UV 移动得到这样的流动效果
最后再用 i.normalDir.y 来区分顶面和侧面
接下来你可以把这个黑白图 当成高光,转换为法线,增亮Diffuse 等等,
如果你用的是一些光照模型,例如PBR 只需要传入相应的通道就可以了
关于法线还有一些特殊的地方 由于是世界空间投影所以 在背面UV会是反向的,对于Diffuse来说 重复的纹理反向的也没什么问题,但对于法线来说给人的凹凸感就是反的 ,所以需要特殊处理一下 ,两个投影面xy ,zy分别是
if(i.normalDir.z < 0)
{
return UV;
}
else
{
float x = UV.x;
x = -x + 1;
return float2(x, UV.y);
}
if(i.normalDir.x > 0)
{
return UV;
}
else
{
float x = UV.x;
x = -x + 1;
return float2(x, UV.y);
}
我们增加上法线后的效果 ,现在根题图效果只差 后处理,粒子系统 了
https://github.com/Unity-Technologies/PostProcessing
使用到的后处理有:
- 屏幕空间反射
- 屏幕空间AO
- 景深
- Color Grading(ACES )
- Bloom
- 粒子系统 :略
贴图打包下载:
链接: pan.baidu.com/s/1jyD5yE 提取码: hmjg
参考:
Rainy Surface Shader Part 1: Ripples
来源知乎作者:面向搜索引擎编程