Unity uGUI用、ゆらぎシェーダー。水面や水中の表現に使えるシェーダー。
(Fluctuation Shader for Unity uGUI)
関連ページ
参考URL
水面や陽炎の表現が出来る、画面がゆらゆら揺らぐようなシェーダーが作りたいなと思ってしばらく研究を続けていた。
幸いこういった表現は既にネットにいくつか記事が転がっており、それを元にuGUI用に調整することができたので共有。
似たカテゴリで
ウェーブシェーダー
もオススメ。
後にこのシェーダーを活かして
陽炎シェーダー
も作ってるのでそちらもどうぞ。
ゆらぎシェーダー、コード全文
今回のシェーダーを実装したサンプル動画は次の通り。
左側のドラゴンに関しては、
シルエットシェーダー
を併用してちょっと水色に画像を上書きしている。
以下コード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
今回の場合GrabPassを使っているので、
こちら
の知識も必要になる。
Shader
"UI/Fluctuation"
{
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
_FluctPow("FluctPow",
Range(0.01,
0.1))
=
0.05
//歪みの強さ
_AnimSpeed("AnimSpeed",
Range(0.1,
10))
=
0.2
//歪みが変化するスピード
[Space(10)]
[Toggle]
_UseAutoAnim("UseAutoAnim",
float)
=
1
//経過時間によって自動でアニメーションをするか
_ManualAnimVal("ManualAnimVal",
float)
=
0
//AutoAnimを使わない場合の、アニメの移動値
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
"RenderType"="Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
LOD
100
GrabPass{}
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
#include
"UnityCG.cginc"
struct
appdata
{
float4
vertex
:
POSITION;
float2
uv
:
TEXCOORD0;
};
struct
v2f
{
float4
vertex
:
POSITION;
float2
uv
:
TEXCOORD0;
};
sampler2D
_GrabTexture;
float
_FluctPow;
float
_AnimSpeed;
float
_UseAutoAnim;
float
_ManualAnimVal;
v2f
vert
(appdata
v)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(v.vertex);
o.uv
=
ComputeGrabScreenPos(o.vertex);
return
o;
}
//ランダムノイズ(粗いノイズ、0~1の値がランダムに広がる)
float
random
(fixed2
p)
{
p
=
fixed2(dot(p,
float2(127.1,
311.7)),
dot(p,
fixed2(269.5,
183.3)));
return
-1.0
+
2.0
*
frac(sin(p)
*
43758.5453123);
}
//パーリンノイズ(隣接する値が少しずつ異なる滑かなノイズ、ランダムノイズを参照する)
float
perlinNoise(fixed2
st)
{
fixed2
p
=
floor(st);
fixed2
f
=
frac(st);
fixed2
u
=
f
*
f
*
(3.0
-
2.0
*
f);
float
v00
=
random(p
+
fixed2(0,
0));
float
v10
=
random(p
+
fixed2(1,
0));
float
v01
=
random(p
+
fixed2(0,
1));
float
v11
=
random(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;
}
//fBmノイズ(究極に滑らかなノイズ、パーリンノイズを参照する)
float
fBm
(fixed2
st)
{
float
f
=
0;
fixed2
q
=
st;
f
+=
0.5000*perlinNoise(
q
);
q
=
q*2.01;
f
+=
0.2500*perlinNoise(
q
);
q
=
q*2.02;
f
+=
0.1250*perlinNoise(
q
);
q
=
q*2.03;
f
+=
0.0625*perlinNoise(
q
);
q
=
q*2.01;
return
f;
}
fixed4
frag
(v2f
i)
:
SV_Target
{
float2
nUv
=
i.uv;
//UseAutoAnimがOnの時は時間経過によってノイズの位置をずらす、Offの時はManualAnimValを参照する
nUv.y
+=
((_UseAutoAnim
*
_Time.y)
+
((1
-
_UseAutoAnim)
*
_ManualAnimVal))
*
_AnimSpeed;
//fBmノイズで滑らかなノイズを作成した上で、値の範囲を0~1から-1~1に拡大
float
noise
=
2
*
fBm(nUv)
-1
;
//画面の上下端では歪みを実行させないためのnoise配合率を算出
fixed
isUvBotom
=
step(i.uv.y,
0.1);
fixed
rate1
=
isUvBotom
*
(i.uv.y
/
0.1)
+
(1
-
isUvBotom)
*
1;
fixed
isUvUp
=
1
-
step(i.uv.y,
0.9);
fixed
rate2
=
isUvUp
*
((0.1
-
(i.uv.y
-
0.9))
/
0.1)
+
(1
-
isUvUp)
*
1;
fixed
noiseRate
=
rate1
*
rate2;
//noiseの値は-1~1の範囲に収まっている
//これに_FluctPowを掛けると、最小で-0.01~0.01、最大で-0.1~0.1の範囲に収まる
//その修正された値で、対象画像のuv座標のy軸だけ移動させ、描画するピクセルをズラす
i.uv.y
+=
noiseRate
*
(noise
*
_FluctPow);
//Grabした画像に変形させたuv座標を適用させ歪ませる
fixed4
col
=
tex2D(_GrabTexture,
i.uv);
return
col;
}
ENDCG
}
}
}
ゆらぎシェーダーの使い方
プロパティとして4つの値が外出しされている。
[HideInInspector]_MainTexはエラーログ回避のため用意してるだけなんで数に入れない。
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
_FluctPow("FluctPow",
Range(0.01,
0.1))
=
0.05
//歪みの強さ
_AnimSpeed("AnimSpeed",
Range(0.1,
10))
=
0.2
//歪みが変化するスピード
[Space(10)]
[Toggle]
_UseAutoAnim("UseAutoAnim",
float)
=
1
//経過時間によって自動でアニメーションをするか
_ManualAnimVal("ManualAnimVal",
float)
=
0
//AutoAnimを使わない場合の、アニメの移動値
}
FluctPowを操作すると画像に対する歪みの強さを変更できる。
下は最小値0.01と最大値0.1に設定した時の比較動画。
AnimSpeedを操作すると歪みの変化スピードを変更できる。
下は最小値の0.1と最大値の10に設定した時の比較動画。
UseAutoAnimのトグルがOnの場合、UnityをPlay状態にしてるだけで勝手にゆらぎアニメーションを実行する。
コードで言うと下の部分で、_Time.yというUnity側で用意している変数を使い、シーン読み込みからの経過時間でゆがみを変化させてる。
//UseAutoAnimがOnの時は時間経過によってノイズの位置をずらす、Offの時はManualAnimValを参照する
nUv.y
+=
((_UseAutoAnim
*
_Time.y)
+
((1
-
_UseAutoAnim)
*
_ManualAnimVal))
*
_AnimSpeed;
Unityのドキュメントを読むと、_Timeは4次元の変数で、1~4次元目まで経過時間を少し割ったり掛けたりしてるだけになる。
_Time.yは名前の印象から受けるy軸とかは関係なくて、単純に2次元目のtを指定してるだけ。
UseAutoAnimは初期状態にOnになっていて、もしOffにした場合は、勝手にアニメーションは実行してくれない。
この場合はManualAnimValを操作することで手動でアニメーションを実行する。
勿論
マテリアルにはC#からアクセス可能
なので、C#のコード上からアニメーションも実行できる。下は一例。
DOTween.To(() => star.material.GetFloat("_ManualAnimVal"), (val) =>
{
star.material.SetFloat("_ManualAnimVal", val);
}, 50, 3f);
1
1