消融效果是模拟物体逐渐从屏幕上消失或溶解的过程,它通常利用噪声纹理实现,使物体按照某种规则逐渐透明或完全不可见。这种效果常用于: 角色死亡、传送场景、 魔法消失,比如燃烧、消失等
1、基本原理
通过对比噪声纹理值与消融进度参数,剔除低于阈值的像素,同时在边缘添加渐变颜色实现动态溶解效果。
关键点:
- 如何剔除像素
在片元着色器中对噪声纹理进行采样,由于噪声纹理是灰度图,只需取出其中的RGB中的任意一通道的颜色来使用。再自定义一个用于控制消融进度的参数(0~1),最后利用该片元的 噪声纹理值 – 进度参数,若小于0则不渲染该片元,通过控制进度参数,便可以控制消融程度了
- 如何处理边缘
在处理边缘渐变颜色效果时,将使用Unity中内置的三个Shader函数:
smoothstep(a, b, x)
a起始值;b结束值;x输入值(用于在a和b之间平滑插值)
当x<a时,返回0;当x>b时,返回1;
a < x < b时,返回0~1之间的值
lerp(a,b,t)
a起始值;b结束值;t插值因子
当t=0时,返回a;当t=1时,返回b;当 0 < t < 1时;返回a和b之间的值
step(value, x)
value阈值,x输入值;两值用于比较
x < value,返回0;x>=value,返回1
首先我们利用smoothstep函数决定边缘颜色,利用噪声颜色值 – 消融进度值 得到一个剔除阈值value,然后自定义一个边缘范围值_Range,然后用smoothstep函数来得到一个值
接着在原本的颜色和渐变颜色之间进行插值,决定使用哪个颜色。
利用smoothstep结合消融阈值来决定在渐变纹理中采用的渐变颜色,利用lerp来决定在原始颜色和边缘渐变颜色中使用哪个颜色,用自定义参数来决定边缘范围,从而实现消融边缘渐变色
2、实现
Shader "ShaderProj/21/Dissolve"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_MainColor ("MainColor", Color) = (1,1,1,1)
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_SpecularColor("SpecularColor", Color) = (1,1,1,1)
_SpecularNum("SpecularNum", Range(8, 256)) = 18
_Noise ("Noise", 2D) = ""{}
_Gradient ("Gradient", 2D) = ""{}
_Dissolve ("Dissolve", Range(0, 1)) = 0
_EdgeRange ("EdgeRange", Range(0, 1)) = 0
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct v2f
{
float4 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 lightDir : TEXCOORD1;
float3 viewDir : TEXCOORD2;
float2 uvNoise : TEXCOORD3;
float3 worldPos : TEXCOORD4;
SHADOW_COORDS(5)
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _MainColor;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _SpecularColor;
fixed _SpecularNum;
sampler2D _Noise;
float4 _Noise_ST;
sampler2D _Gradient;
fixed _Dissolve;
fixed _EdgeRange;
v2f vert (appdata_full v)
{
v2f data;
data.pos = UnityObjectToClipPos(v.vertex);
data.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
data.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
data.uvNoise = TRANSFORM_TEX(v.texcoord, _Noise);
float3 binormal = cross(normalize(v.tangent), normalize(v.normal)) * v.tangent.w;
float3x3 rotation = float3x3(v.tangent.xyz,
binormal,
v.normal);
data.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
data.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
data.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(data);
return data;
}
fixed4 frag (v2f i) : SV_Target
{
// 剔除
fixed3 noiseColor = tex2D(_Noise, i.uvNoise);
clip(_Dissolve == 1 ? -1 : noiseColor - _Dissolve);
float4 packedNormal = tex2D(_BumpMap, i.uv.zw);
float3 tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, i.uv.xy) * _MainColor.rgb;
fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(tangentNormal, normalize(i.lightDir)));
float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir));
fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(tangentNormal, normalize(i.viewDir))), _SpecularNum);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten + specularColor;
// 渐变颜色
fixed t = 1 - smoothstep(0, _EdgeRange, noiseColor - _Dissolve);
fixed3 gradient = tex2D(_Gradient, fixed2(t, 0));
fixed3 finalColor = lerp(color, gradient, t * step(0.00001, _Dissolve));
return fixed4(finalColor, 1);
}
ENDCG
}
Pass{
Tags{"LightMode" = "ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
fixed2 uvNoise : TEXCOORD0;
};
sampler2D _Noise;
float4 _Noise_ST;
float _Dissolve;
v2f vert(appdata_base v)
{
v2f data;
data.uvNoise = TRANSFORM_TEX(v.texcoord, _Noise);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(data);
return data;
}
fixed4 frag(v2f i) : SV_Target
{
// 剔除影子
fixed3 noiseColor = tex2D(_Noise, i.uvNoise);
clip(_Dissolve == 1 ? -1 : noiseColor - _Dissolve);
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
}
}