Unity Shader-死亡溶解效果(轉(zhuǎn))

簡介

游戲里面角色死亡的時候,有很多方式可以表現(xiàn)人物死亡效果。最簡單粗暴的,播放完死亡動畫直接刪除或者直接Y軸逐漸降低,沉到地下;比較好的死亡效果就是今天要研究的這種效果-死亡溶解。個人印象比較深刻的就是《生化危機6》里面的克里斯篇,里面的功夫喪尸掛掉之后都是會有一個類似的死亡溶解效果。不過《生化危機6》這種3A大作的效果肯定不僅僅是一個shader能做出來的,還配合了粒子做出了消散以及著火的效果。

趕腳一般學習shader的童鞋,都會研究一下這個東東....畢竟這個效果灰常常用。今天,我也來研究一下死亡溶解效果的實現(xiàn)。

基本溶解效果

溶解,也就是讓這個模型逐漸消失。那么,最簡單的,直接讓這個像素的Fragment Shader操作discard,這個像素就消失了。然后,我們要做的就是讓這個溶解的對象一部分消失,另一部分存在,所以,這個時候我們就需要一個Mask圖進行控制,我們采樣這張Mask圖,就可以得到這個像素點當前的Mask值,然后用這個Mask值與我們設定的一個閾值來進行比較,小于閾值的部分discard,大于的部分正常計算。最終,我們將這個閾值從0逐漸增加到1,就可以實現(xiàn)模型的一部分像素先消失,直至整個模型完全消失的效果。

簡單的原理解釋完了,先來一發(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光照計算

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"

}

最近找了個比較帥氣的模型,先爆個照(開啟了BloomDepth Of Field):

然后,讓偶們把它溶解掉,這里我們直接使用了一張噪聲圖,最簡單的雪花點的那種就可以啦,逐漸調(diào)大Dissolve Threshold,就可以了:

增加過渡的溶解效果

上面的溶解效果感覺就像被打成篩子一樣...木有明顯的過渡,感覺效果不是很好。下面寫一個增加了過渡的溶解效果,讓被溶解的部分邊緣變成我們設置的顏色,為了增加豐富性,我們可以增加兩個過渡顏色。分別設置兩個閾值。首先是當閾值小于顏色A閾值的時候,返回顏色A,閾值小于顏色B的時候,返回顏色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光照計算

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"

}

上面的模型,我們配置一個雷電類型的溶解效果,兩個顏色值分別給白色和藍色。然后噪聲圖給一張過渡更加柔和一些的,如下圖:

來一張動圖,先逐漸增大溶解閾值,就是溶解效果;然后再減小閾值,就是閃亮登場的效果:

上面的shader雖然比較容易理解,我們直接用了兩個if進行判斷的。不過感覺shader里面的分支還是盡量少,畢竟如果有動態(tài)分支(編譯期的有些是可以自動優(yōu)化掉的,比如我們寫的固定次數(shù)的循環(huán),這種應該是會直接在編譯期展開了),運行時的shader有動態(tài)分支的話,是會先執(zhí)行一遍A分支,然后再去執(zhí)行B分支,效率就下去了,可以參考知乎大神們的討論。so...我們還是老老實實地寫一發(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光照計算

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;

//如果當前百分比 - 顏色權重 - 邊緣顏色

float lerpEdge = sign(percentage - _ColorFactor - _DissolveEdge);

//貌似sign返回的值還得saturate一下,否則是一個很奇怪的值

fixed3 edgeColor = lerp(_DissolveEdgeColor.rgb, _DissolveColor.rgb, saturate(lerpEdge));

//最終輸出顏色的lerp值

float lerpOut = sign(percentage - _ColorFactor);

//最終顏色在原顏色和上一步計算的顏色之間差值(其實經(jīng)過saturate(sign(..))的lerpOut應該只能是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"

}

效果與上面基本一致,所以這次我們換個顏色再來一發(fā)燃燒溶解效果,把顏色調(diào)整成黃色和紅色:

再來一發(fā)動圖:

不僅僅是溶解,還要灰飛煙滅

轉(zhuǎn)自:http://m.blog.csdn.net/puppet_master/article/details/72455945

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容