原创作品

Shader效果:水果上蠕动的蛆虫——代码讲解篇

唐福幸 发表于   2021-10-29 11:04:48
2517
1
2

资源准备

本Shader的编写逻辑还是跟之前一样的,所以需要准备的贴图也是跟之前一样

1.颜色贴图:我现在不用蛆虫了,用地砖


2.法线贴图:还是用来产生扭曲效果的


3.Mask:表示水坑的位置


唯一不同的是,为了体现水坑的水面效果,我有加了一个cubemap作为环境反射。


下面开始进入代码讲解部分

定义属性

Shader "Custom/Puddle"{
Properties
{
[Header(Color Properties)]
_MainTex ("Main Texture", 2D) = "gray" {}

// 波纹属性 [Space(20)]
[Header(Ripple Properties)]
_Normal ("Normal", 2D) = "bump" {}
_NormalIntensity ("Normal Intensity", Range(0, 1)) = 0
_NormalSpeedX ("Normal Speed X", Range(0,1)) = 0
_NormalSpeedY ("Normal Speed Y", Range(0,1)) = 0

// 水坑属性 [Space(20]
[Header(Puddle Properties)]
[NoScaleOffset]_NormalMask ("Normal Mask", 2D) = "white" {}
_Depth ("Depth", Range(0, 1)) = 0

// 反射属性 [Space(20)]
[Header(reflection Properties)]
[NoScaleOffset]_Cubemap ("Cubemap", Cube) = "" {}
_Reflection ("Reflection", Range(0, 1)) = 0
}

为了使用的时候更好的调节,这次开放的属性比较多。

[Header(######)]:在属性面板上添加标题,方便对不同类型的属性进行区分


[Space(count)]:在属性之间添加空行,还是为了隔开不同类型的属性


[NoScaleOffset]:在材质面板上隐藏Tiling和Offset


_NormalIntensity :用于控制扭曲的强度


_NormalSpeedX :用于控制横向扭曲的速度


_NormalSpeedY :用于控制纵向扭曲的速度


_Depth :用于控制水坑位置颜色的深度


_Reflection :用于控制反射强度

结构体和CG变量

SubShader{
Tags {"Queue" = "Geometry"}

Pass
{
CGPROGRAM
#pragma vertex vert #pragma fragment frag #include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;

float4 mainnormalCoord : TEXCOORD0; // 颜色和法线的纹理坐标 float2 maskCoord : TEXCOORD1; // Mask的纹理坐标
float4 worldPos : TEXCOORD2; // 世界空间顶点坐标 float3 worldNormal : TEXCOORD3; // 世界空间法线坐标 };

sampler2D _MainTex;
float4 _MainTex_ST;

sampler2D _Normal;
float4 _Normal_ST;
fixed _NormalIntensity;
fixed _NormalSpeedX;
fixed _NormalSpeedY;

sampler2D _NormalMask;
fixed _Depth;

samplerCUBE _Cubemap;
fixed _Reflection;

结构体存储了如下内容:

1.裁切空间顶点坐标


2.颜色贴图纹理坐标


3.法线贴图纹理坐标


4.Mask贴图纹理坐标


5.世界空间顶点坐标


6.世界空间法线坐标

其中,为了减少内插器的使用数量,颜色贴图和法线贴图的纹理坐标一起存到了mainnormalCoord 中。

顶点着色器

v2f vert (appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex.xyz);

// 计算贴图的纹理坐标 o.mainnormalCoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.mainnormalCoord.zw = TRANSFORM_TEX(v.texcoord, _Normal);
o.mainnormalCoord.zw += float2(_NormalSpeedX, _NormalSpeedY) * _Time.x;
o.maskCoord = v.texcoord.xy;

o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));

return o;}

在顶点着色器中,分别进行了如下计算

1.得到裁切空间顶点坐标


2.得到颜色贴图和法线贴图的纹理坐标


3.将法线贴图纹理坐标通过_Time变量进行不断偏移


4.得到Mask贴图纹理坐标


5.得到世界空间顶点坐标


6.得到世界空间法线坐标

片段着色器

                        fixed4 frag (v2f i) : SV_Target
{
// 计算法线强度 fixed2 normal = UnpackNormal(tex2D(_Normal, i.mainnormalCoord.zw)).xy;
fixed mask = tex2D(_NormalMask, i.maskCoord);
normal *= _NormalIntensity * mask * 0.01;

// 计算颜色 float2 mainCoord = i.mainnormalCoord.xy + normal;
fixed4 color = tex2D(_MainTex, mainCoord);
color.rgb -= (mask * _Depth);

// 计算反射 float3 viewDir = UnityWorldSpaceViewDir(i.worldPos.xyz);
viewDir = normalize(viewDir);
fixed3 refDir = reflect(-viewDir.xyz, i.worldNormal);
fixed4 ref = texCUBE(_Cubemap, refDir);

// 颜色与反射混合 return lerp(color, ref, mask * _Reflection);
}
ENDCG
}
}}

在片段着色器中,先对法线贴图和Mask贴图进行了采样。法线乘以_NormalIntensity 用于控制法线强度,再乘以mask,使只有水坑的位置才会受法线影响。

用上一步得到的normal对颜色贴图的纹理坐标进行偏移,得到mainCoord 变量,然后再使用这个变量对颜色贴图进行采样,最后减去mask乘以_Depth的值,使水坑部位的颜色变暗。

使用世界空间顶点坐标得到时间空间视角方向,然后再通过reflect()函数得到反射方向,最后使用texCUBE()函数对cubemap进行采样,得到环境反射。

最后使用lerp()函数在颜色和反射之间进行插值运算,_Reflection控制反射强度,之所以再乘以mask是为了让除水坑的部位不进行反射。


最终材质面板

按照材质面板上的参数,即可调出视频里的效果。



没有标签
确定
评论(1)
86
有效果得视频就好了。
回复
1360天前
唐福幸
回复
86
在前面一篇
回复
1360天前
86
回复
唐福幸
看到了,这效果真细致
回复
1359天前
没有更多啦~