UnityでuGUI用溶解シェーダー。画像が徐々に溶けていくような演出。
(Dissolve Shader for Unity uGUI to gradual melting of images)
関連ページ
参考URL
前回
ドット欠けシェーダー
を作った際に、ランダムノイズという関数について学習した。
この関数をパーリンノイズという複雑な関数に入れ替えてみた所、画像が溶け出すような、世間で言うDissolve Shaderが簡単に作れた。
今回は
こちらの記事
と
こちらの記事
を参考しにしている。パーリンノイズの関数内で何が行われてるかは正直理解してない。
溶解シェーダー、コード全文
下は溶解シェーダーのサンプル動画。
以下コード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
Shader
"UI/Dissolve"
{
Properties
{
_DissolvePow
("DissolvePow",
Range(0.0,
1.0))
=
0
//溶けていく力、1で完全に消滅する
_Spot
("Spot",
Range(1,
50))
=
10
//溶けていく際の穴の数
_Roughness
("Roughness",
Range(1,
20.0))
=
1
//欠けていくドットの粗さ
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
struct
appdata
{
fixed2
uv
:
TEXCOORD0;
fixed4
vertex
:
POSITION;
fixed4
color
:
COLOR;
};
struct
v2f
{
fixed2
uv
:
TEXCOORD0;
fixed4
vertex
:
POSITION;
fixed4
color
:
COLOR;
};
sampler2D
_MainTex;
//アタッチされたSpriteのサイズを取得(x=1.0/width, y=1.0/height, z=width, w=height)
float4
_MainTex_TexelSize;
float
_DissolvePow;
float
_Spot;
float
_Roughness;
v2f
vert
(appdata
v)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(v.vertex);
o.uv
=
v.uv;
o.color
=
v.color;
return
o;
}
//2次元 疑似ランダム(入力は2次元です)
float2
random2(fixed2
st)
{
st
=
fixed2(dot(st,
float2(127.1,
311.7)),
dot(st,
fixed2(269.5,
183.3)));
return
-1.0
+
2.0
*
frac(sin(st)
*
43758.5453123);
}
//パーリンノイズ (上記のrandom2を使います)
float
perlinNoise(fixed2
st)
{
fixed2
p
=
floor(st);
fixed2
f
=
frac(st);
fixed2
u
=
f
*
f
*
(3.0
-
2.0
*
f);
float
v00
=
random2(p
+
fixed2(0,
0));
float
v10
=
random2(p
+
fixed2(1,
0));
float
v01
=
random2(p
+
fixed2(0,
1));
float
v11
=
random2(p
+
fixed2(1,
1));
return
lerp(lerp(dot(v00,
f
-
fixed2(0,
0)),
dot(v10,
f
-
fixed2(1,
0)),
u.x),
lerp(dot(v01,
f
-
fixed2(0,
1)),
dot(v11,
f
-
fixed2(1,
1)),
u.x),
u.y)
+
0.5f;
}
fixed4
frag
(v2f
i)
:
SV_Target
{
fixed4
col
=
tex2D(_MainTex,
i.uv);
//アタッチされたSpriteの縦横比をratioに保存
fixed
ratio
=
_MainTex_TexelSize.z
/
_MainTex_TexelSize.w;
//_Roughnessの入力値を実際に使う値まで細かくする
fixed
fixRough
=
_Roughness
*
0.001f;
//対象ピクセルのx座標とy座標を保存、ただしfixRoughの値に応じて座標の取得が大雑把になる
//またy座標にはratioを追加で掛けることで、座標を大雑把にした時の値に補正を乗せて欠けるドットを正方形に修正する
fixed
roughX
=
floor(i.uv.x
/
fixRough)
*
fixRough;
fixed
roughY
=
floor(i.uv.y
/
(fixRough
*
ratio))
*
(fixRough
*
ratio);
fixed2
roughUv
=
fixed2(roughX,
roughY);
//パーリンノイズにより、指定座標に対し(ほぼ)0~1のランダムな値が返ってくる
fixed
rdm
=
perlinNoise(roughUv
*
_Spot);
//パーリンノイズから_DisolvePowを引き、その値が-0.1未満になった時対象ドットを消滅させる
fixed
doVanish
=
step(-0.1,
rdm
-
_DissolvePow
*
1.2);
col
*=
i.color;
col.a
=
col.a
*
doVanish;
return
col;
}
ENDCG
}
}
}
プロパティを解説
用意してあるプロパティは3つ。
Roughnessを上げる際、ドット欠けシェーダーと同様にImageの設定によっては欠けていくドットが長方形になる。
またRoughnessの値は大きくしすぎるとDissolveの良さが失われるので、最大値が少し抑え目にしてある。
コードを少し解説
ランダムノイズ関数をパーリンノイズ関数に変えてるだけなので、あんまり解説するところがない。
違うところだけ少し解説。なおドット欠けシェーダーのコードの理解を前提としている。
//パーリンノイズにより、指定座標に対し(ほぼ)0~1のランダムな値が返ってくる
fixed
rdm
=
perlinNoise(roughUv
*
_Spot);
//パーリンノイズから_DisolvePowを引き、その値が-0.1未満になった時対象ドットを消滅させる
fixed
doVanish
=
step(-0.1,
rdm
-
_DissolvePow
*
1.2);
step関数内の引数で、-0.1やら* 1.2やらのマジックナンバーが使われている。
これは指定した_Spotの値によっては、perlinNoise関数から返ってくる値が0をわずかに割ってマイナス値が返ってきてしまうため。
stepの第一引数を0にしてしまうと、たとえ_DissolvePowが0でも最初からいくつかピクセルが欠けてる状態になってしまう。
第一引数を-0.1にすることで、わずかにマイナスに転んでしまったrdmの値にも対応している。
しかし第一引数を-0.1にした結果、今度は逆に_DissolvePowを最大値の1にしても画像が全て消えない状態になる。
このため第二引数の_DissolvePowを1.2倍にして無理やり解決してる。
抜本的な治療にはperlinNoise関数内を真面目に解析する必要があるが、そこまでの気力が起きなかった。
1
1