簡介
游戲里面角色死亡的時(shí)候诚隙,有很多方式可以表現(xiàn)人物死亡效果献雅。最簡單粗暴的昵仅,播放完死亡動畫直接刪除或者直接Y軸逐漸降低斗遏,沉到地下;比較好的死亡效果就是今天要研究的這種效果-死亡溶解梅猿。個(gè)人印象比較深刻的就是《生化危機(jī)6》里面的克里斯篇氓辣,里面的功夫喪尸掛掉之后都是會有一個(gè)類似的死亡溶解效果。不過《生化危機(jī)6》這種3A大作的效果肯定不僅僅是一個(gè)shader能做出來的袱蚓,還配合了粒子做出了消散以及著火的效果钞啸。
趕腳一般學(xué)習(xí)shader的童鞋,都會研究一下這個(gè)東東....畢竟這個(gè)效果灰常常用喇潘。今天体斩,我也來研究一下死亡溶解效果的實(shí)現(xiàn)。
基本溶解效果
溶解颖低,也就是讓這個(gè)模型逐漸消失絮吵。那么,最簡單的忱屑,直接讓這個(gè)像素的Fragment Shader操作discard蹬敲,這個(gè)像素就消失了。然后莺戒,我們要做的就是讓這個(gè)溶解的對象一部分消失伴嗡,另一部分存在,所以从铲,這個(gè)時(shí)候我們就需要一個(gè)Mask圖進(jìn)行控制瘪校,我們采樣這張Mask圖,就可以得到這個(gè)像素點(diǎn)當(dāng)前的Mask值,然后用這個(gè)Mask值與我們設(shè)定的一個(gè)閾值來進(jìn)行比較阱扬,小于閾值的部分discard泣懊,大于的部分正常計(jì)算。最終麻惶,我們將這個(gè)閾值從0逐漸增加到1馍刮,就可以實(shí)現(xiàn)模型的一部分像素先消失,直至整個(gè)模型完全消失的效果用踩。
簡單的原理解釋完了渠退,先來一發(fā)基本的溶解效果:
//溶解效果
//by:puppet_master
//2017.5.18
Shader "ApcShader/DissolveEffect"
{
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_MainTex("Base 2D", 2D) = "white"{}
_DissolveMap("DissolveMap", 2D) = "white"{}
_DissolveThreshold("DissolveThreshold", Range(0,1)) = 0
}
CGINCLUDE
#include "Lighting.cginc"
uniform fixed4 _Diffuse;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform sampler2D _DissolveMap;
uniform float _DissolveThreshold;
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//采樣Dissolve Map
fixed4 dissolveValue = tex2D(_DissolveMap, i.uv);
//小于閾值的部分直接discard
if (dissolveValue.r < _DissolveThreshold)
{
discard;
}
//Diffuse + Ambient光照計(jì)算
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;
return fixed4(color, 1);
}
ENDCG
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack "Diffuse"
}
最近找了個(gè)比較帥氣的模型,先爆個(gè)照(開啟了Bloom和Depth Of Field):
然后脐彩,讓偶們把它溶解掉碎乃,這里我們直接使用了一張?jiān)肼晥D,最簡單的雪花點(diǎn)的那種就可以啦惠奸,逐漸調(diào)大Dissolve Threshold梅誓,就可以了:
增加過渡的溶解效果
上面的溶解效果感覺就像被打成篩子一樣...木有明顯的過渡,感覺效果不是很好佛南。下面寫一個(gè)增加了過渡的溶解效果梗掰,讓被溶解的部分邊緣變成我們設(shè)置的顏色,為了增加豐富性嗅回,我們可以增加兩個(gè)過渡顏色及穗。分別設(shè)置兩個(gè)閾值。首先是當(dāng)閾值小于顏色A閾值的時(shí)候绵载,返回顏色A埂陆,閾值小于顏色B的時(shí)候,返回顏色B娃豹,否則返回原始顏色焚虱。shader代碼如下:
//溶解效果
//by:puppet_master
//2017.5.18
Shader "ApcShader/DissolveEffect"
{
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_DissolveColorA("Dissolve Color A", Color) = (0,0,0,0)
_DissolveColorB("Dissolve Color B", Color) = (1,1,1,1)
_MainTex("Base 2D", 2D) = "white"{}
_DissolveMap("DissolveMap", 2D) = "white"{}
_DissolveThreshold("DissolveThreshold", Range(0,1)) = 0
_ColorFactorA("ColorFactorA", Range(0,1)) = 0.7
_ColorFactorB("ColorFactorB", Range(0,1)) = 0.8
}
CGINCLUDE
#include "Lighting.cginc"
uniform fixed4 _Diffuse;
uniform fixed4 _DissolveColorA;
uniform fixed4 _DissolveColorB;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform sampler2D _DissolveMap;
uniform float _DissolveThreshold;
uniform float _ColorFactorA;
uniform float _ColorFactorB;
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//采樣Dissolve Map
fixed4 dissolveValue = tex2D(_DissolveMap, i.uv);
//小于閾值的部分直接discard
if (dissolveValue.r < _DissolveThreshold)
{
discard;
}
//Diffuse + Ambient光照計(jì)算
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;
//這里為了比較方便,直接用color和最終的邊緣lerp了
float lerpValue = _DissolveThreshold / dissolveValue.r;
if (lerpValue > _ColorFactorA)
{
if (lerpValue > _ColorFactorB)
return _DissolveColorB;
return _DissolveColorA;
}
return fixed4(color, 1);
}
ENDCG
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack "Diffuse"
}
上面的模型懂版,我們配置一個(gè)雷電類型的溶解效果鹃栽,兩個(gè)顏色值分別給白色和藍(lán)色。然后噪聲圖給一張過渡更加柔和一些的躯畴,如下圖:
來一張動圖民鼓,先逐漸增大溶解閾值,就是溶解效果私股;然后再減小閾值摹察,就是閃亮登場的效果:
上面的shader雖然比較容易理解,我們直接用了兩個(gè)if進(jìn)行判斷的倡鲸。不過感覺shader里面的分支還是盡量少,畢竟如果有動態(tài)分支(編譯期的有些是可以自動優(yōu)化掉的黄娘,比如我們寫的固定次數(shù)的循環(huán)峭状,這種應(yīng)該是會直接在編譯期展開了)克滴,運(yùn)行時(shí)的shader有動態(tài)分支的話,是會先執(zhí)行一遍A分支优床,然后再去執(zhí)行B分支劝赔,效率就下去了,可以參考知乎大神們的討論胆敞。so...我們還是老老實(shí)實(shí)地寫一發(fā)用值來判斷的(除了discard):
//溶解效果
//by:puppet_master
//2017.5.19
Shader "ApcShader/DissolveEffect"
{
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_DissolveColor("Dissolve Color", Color) = (0,0,0,0)
_DissolveEdgeColor("Dissolve Edge Color", Color) = (1,1,1,1)
_MainTex("Base 2D", 2D) = "white"{}
_DissolveMap("DissolveMap", 2D) = "white"{}
_DissolveThreshold("DissolveThreshold", Range(0,1)) = 0
_ColorFactor("ColorFactor", Range(0,1)) = 0.7
_DissolveEdge("DissolveEdge", Range(0,1)) = 0.8
}
CGINCLUDE
#include "Lighting.cginc"
uniform fixed4 _Diffuse;
uniform fixed4 _DissolveColor;
uniform fixed4 _DissolveEdgeColor;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform sampler2D _DissolveMap;
uniform float _DissolveThreshold;
uniform float _ColorFactor;
uniform float _DissolveEdge;
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float2 uv : TEXCOORD1;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//采樣Dissolve Map
fixed4 dissolveValue = tex2D(_DissolveMap, i.uv);
//小于閾值的部分直接discard
if (dissolveValue.r < _DissolveThreshold)
{
discard;
}
//Diffuse + Ambient光照計(jì)算
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;
//優(yōu)化版本着帽,盡量不在shader中用分支判斷的版本,但是代碼很難理解啊....
float percentage = _DissolveThreshold / dissolveValue.r;
//如果當(dāng)前百分比 - 顏色權(quán)重 - 邊緣顏色
float lerpEdge = sign(percentage - _ColorFactor - _DissolveEdge);
//貌似sign返回的值還得saturate一下,否則是一個(gè)很奇怪的值
fixed3 edgeColor = lerp(_DissolveEdgeColor.rgb, _DissolveColor.rgb, saturate(lerpEdge));
//最終輸出顏色的lerp值
float lerpOut = sign(percentage - _ColorFactor);
//最終顏色在原顏色和上一步計(jì)算的顏色之間差值(其實(shí)經(jīng)過saturate(sign(..))的lerpOut應(yīng)該只能是0或1)
fixed3 colorOut = lerp(color, edgeColor, saturate(lerpOut));
return fixed4(colorOut, 1);
}
ENDCG
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack "Diffuse"
}
效果與上面基本一致移层,所以這次我們換個(gè)顏色再來一發(fā)燃燒溶解效果仍翰,把顏色調(diào)整成黃色和紅色:
再來一發(fā)動圖:
不僅僅是溶解,還要灰飛煙滅
轉(zhuǎn)自:http://m.blog.csdn.net/puppet_master/article/details/72455945