UnityShader实例08:溶解消融(Dissolve)材质

时间:2022-07-02 03:46:52

溶解消融(Dissolve)材质

在不少3D游戏中,角色死亡后会有一个溶解消融的特效,这个除了粒子特效以外还需要shader的配合。下面就是本例实现的一个效果,当然没有例子配合看起来是搓搓的,不过效果基本上已经出来了

UnityShader实例08:溶解消融(Dissolve)材质


shader实现原理


消融效果需要用到的CG函数Clip(),这个函数的定义是如果输入向量的任何一个元素小于0,则抛弃当前像素;所以要实现上图逐渐消融的效果,我们需要一个变化值和一张噪波图(如下图)比较,根据差值变化来抛弃像素。

UnityShader实例08:溶解消融(Dissolve)材质


shader代码实现


首先需要在属性里面声明几个控制参数:

_DissolveTex ("DissolveTex (RGB)", 2D) = "white" //噪波图,
_Tile("DissolveTile", Range (0.1, 1)) = 1 //噪波图的平铺系数,平铺倍数与之成反比

_Amount ("DissAmount", Range (0, 1)) = 0.5 //溶解值,低于这个值,像素将被抛弃
_DissSize("DissSize", Range (0, 1)) = 0.1 //预溶解范围大小

_DissColor ("DissColor", Color) = (1,0,0,1)//预溶解范围渐变颜色,与_AddColor配合形成渐变色
_AddColor ("AddColor", Color) = (1,1,0,1)


接下来的工作主要是在Frag函数里面处理,关键代码如下:

//对主纹理采样
fixed4 col = tex2D(_MainTex, i.texcoord);
//对噪波贴图进行采样,取R值
float ClipTex = tex2D (_DissolveTex, i.texcoord/_Tile).r;
//获取裁剪量
float ClipAmount = ClipTex - _Amount;
if(_Amount > 0)
{
//噪波图中R通道颜色值低于外部量_Amount,则被裁剪
if(ClipAmount < 0)
{
clip(-0.1);
}
//然后处理没被裁剪的值
else{
if(ClipAmount < _DissSize)
{
//针对没被裁剪的点,如果裁剪量小于预溶解范围则使用lerp函数做渐变处理
float4 finalColor=lerp(_DissColor,_AddColor,ClipAmount/_DissSize)*2;
//将获得的渐变颜色与主颜色叠加融合
col = col * finalColor;
}
}
}

VF版本代码01:

// - no lighting
// - no lightmap support
// - no per-material color

Shader "PengLu/Unlit/DissolveVF" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_DissolveTex ("DissolveTex (RGB)", 2D) = "white" {}
_Tile("DissolveTile", Range (0.1, 1)) = 1

_Amount ("DissAmount", Range (0, 1)) = 0.5
_DissSize("DissSize", Range (0, 1)) = 0.1

_DissColor ("DissColor", Color) = (1,0,0,1)
_AddColor ("AddColor", Color) = (1,1,0,1)
}

SubShader {
Tags { "RenderType"="Opaque" }
LOD 100

Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog

#include "UnityCG.cginc"

sampler2D _MainTex,_DissolveTex;
fixed4 _MainTex_ST,_DissolveTex_ST;
half _Tile,_Amount,_DissSize;
half4 _DissColor,_AddColor;


struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};

struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
};

v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
float ClipTex = tex2D (_DissolveTex, i.texcoord/_Tile).r;
float ClipAmount = ClipTex - _Amount;
if(_Amount > 0)
{
if(ClipAmount < 0)
{
clip(-0.1);
}
else{
if(ClipAmount < _DissSize)
{
float4 finalColor=lerp(_DissColor,_AddColor,ClipAmount/_DissSize)*2;
col = col * finalColor;
}
}
}
UNITY_APPLY_FOG(i.fogCoord, col);
UNITY_OPAQUE_ALPHA(col.a);
return col;
}
ENDCG
}
}

}