Unity uGUI用、レンズフレアシェーダー。太陽の光を表現。
(Lens Flare Shader for Unity uGUI)
関連ページ
前から陽炎の演出をシェーダーで作成できないかと思っていて、そのためにはまず太陽の輝きを表現するレンズフレアの作成が不可欠だった。
大分苦労するかなと思いきや、あれこれ試行錯誤して意外にそれっぽい演出が作れた。
このシェーダーを作成した後、元々の目的であった
陽炎シェーダー
も作ってるのでそちらもどうぞ。
レンズフレアシェーダー概要
レンズフレアとは、写真や動画を光源付近に向けて撮った時によく画面に映りこむ光線のこと。
この光は単純な白色ではなく、なんか繊細微妙な、多彩で複雑な色を含んでいる。
人間の目ではこういう現象は起きないが、太陽光の表現としてかなり一般的に周知されている。
今回作ったレンズフレアシェーダーのサンプル動画は次の通り。
C#からシェーダー変数にアクセス
することで演出を実装している。
このシェーダーは9個の変数を用意していて、組み合わせ次第で割と複雑な演出も作成できると思う。
レンズフレアシェーダーの研究している人は結構いるので、まずそのコードを勉強しようと思ったものの、高度な数学を使っていて理解不能だった。
そのため本シェーダーでは早々にインプット作業を諦めて、過去に作った
虹色シェーダー
を応用する方法に切り替えた。
光の繊細微妙な調子は、論理的には作れなかったで感覚でそれっぽく整えている。
厳密にはレンズフレアは円ではなく六角形を作る必要があるらしいけど、そこまでやる気は起きなかった。
レンズフレアシェーダー、コード全文
以下コード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
今回の場合GrabPassを使っているので、
こちら
の知識も必要になる。
Shader
"UI/LensFlare"
{
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
_Degree("Degree",
Range(0,
359))
=
90
//光線の角度を指定
_FlareLength("FlareLength",
Range(0,
1))
=
1
//光線の長さを指定
_FlareConverge("FlareConverge",
Range(0,
1))
=
1
//大きいほど光が端に寄り小さいほど中央に寄る
[Space(10)]
_FlareSize("FlareSize",
Range(0.1,
2))
=
1
//レンズフレアの大きさ
_FlareLuminance("FlareLuminance",
Range(0.1,
2))
=
1
//レンズフレアの光の強さ
_FlareHuePlus("FlareHuePlus",
Range(-1,
1))
=
0
//レンズフレアの虹色を変化させる
[Space(10)]
_Brightness("Brightness",
Range(1,
2))
=
1
//画面全体の輝き
_OverrideCol("OverrideCol",
Color)
=
(1.0,
1.0,
1.0,
1.0)
//画面全体を単一色で上書きする際の色
_OverrideRate("OverrideRate",
Range(0,
1))
=
0
//画面全体を単一色で上書きする配合率
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
"RenderType"="Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
LOD
100
CGINCLUDE
#pragma
vertex
vert
#pragma
fragment
frag
#include
"UnityCG.cginc"
struct
appdata
{
float4
vertex
:
POSITION;
float2
uv
:
TEXCOORD0;
};
struct
v2f
{
float4
vertex
:
SV_POSITION;
float2
uv
:
TEXCOORD0;
};
sampler2D
_MainTex;
sampler2D
_GrabTexture;
fixed
_Degree;
fixed
_FlareLength;
fixed
_FlareConverge;
fixed
_FlareSize;
fixed
_FlareLuminance;
fixed
_FlareHuePlus;
fixed
_Brightness;
fixed4
_OverrideCol;
fixed
_OverrideRate;
v2f
vert(appdata
v)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(v.vertex);
o.uv
=
ComputeGrabScreenPos(o.vertex);
return
o;
}
//光線の長さを修正
fixed2
reflectLength(fixed
baseX,
fixed
baseY)
{
fixed
remainX
=
saturate(0.5
-
baseX)
*
(1
-
_FlareLength);
fixed
remainY
=
saturate(0.5
-
baseY)
*
(1
-
_FlareLength);
return
fixed2(baseX
+
remainX,
baseY
+
remainY);
}
//光線を適切に中央に寄せる
fixed2
reflectConverge(fixed
baseX,
fixed
baseY)
{
return
fixed2(baseX
*
_FlareConverge,
baseY
*
_FlareConverge);
}
//_Degreeの入力値に合わせて座標を適切に変換
//baseX = 右上にレンズフレアがある時のx軸のベース座標
//baseY = 右上にレンズフレアがある時のy軸のベース座標
fixed2
reflectDegree(fixed
baseX,
fixed
baseY)
{
//_Degree=0が右上になってるので、少し計算が特殊になってる
//画面を左45度傾けたとして、_Degree値が中心から右に寄ってるか。180で割るとバグるので179.9で代替え
fixed
isRight
=
step(_Degree
/
179.9,
1);
//画面を左45度傾けたとして、_Degree値が中心から上に寄ってるか。90で割るとバグるので89.9で代替え
fixed
isUp
=
isRight
*
step(_Degree
%
179.9
/
89.9,
1)
+
(1
-
isRight)
*
(1
-
step(_Degree
%
179.9
/
89.9,
1));
//画面を4つの区間に分割
fixed
isRightUp
=
isRight
*
isUp;
fixed
isRightDown
=
isRight
*
(1
-
isUp);
fixed
isLeftDown
=
(1
-
isRight)
*
(1
-
isUp);
fixed
isLeftUp
=
(1
-
isRight)
*
isUp;
//0~1の値を返す。_Degreeが45, 135, 225, 315の時transは(ほぼ)ゼロになる
fixed
trans
=
(45
-
_Degree
%
89.9)
/
45;
//x軸の座標を修正
fixed
fixX
=
isRightUp
*
baseX
+
isRightDown
*
baseX
*
trans
+
isLeftDown
*
-baseX
+
isLeftUp
*
-baseX
*
trans;
//y軸の座標を修正
fixed
fixY
=
isRightUp
*
baseY
*
trans
+
isRightDown
*
-baseY
+
isLeftDown
*
-baseY
*
trans
+
isLeftUp
*
baseY;
return
fixed2(fixX,
fixY);
}
//レンズフレアの座標修正
fixed2
fixFlarePos(fixed
x,
fixed
y)
{
fixed2
fixUv
=
reflectConverge(x,
y);
fixUv
=
reflectLength(fixUv.x,
fixUv.y);
fixUv
=
reflectDegree(fixUv.x,
fixUv.y);
return
fixUv;
}
//レンズフレアの大きさを修正
fixed2
reflectSize(fixed
size)
{
return
size
*
_FlareSize;
}
//floatをRGBに変換
fixed3
HUEtoRGB(in
float
H)
{
float
R
=
abs(H
*
6
-
3)
-
1;
float
G
=
2
-
abs(H
*
6
-
2);
float
B
=
2
-
abs(H
*
6
-
4);
return
saturate(float3(R,
G,
B));
}
//レンズフレアの円を作成する
//shiftX = 画面中央からどれだけズレるか、-0.5~0.5の値を受け入れる
//shiftY = 画面中央からどれだけズレるか、-0.5~0.5の値を受け入れる
fixed4
createFlare(v2f
IN,
fixed
size,
fixed
shiftX,
fixed
shiftY,
fixed
hueBlendRate,
fixed
overrideRate,
fixed
hueOffset,
fixed
inStartAlpha,
fixed
inRangeAlpha,
fixed
outStartAlpha,
fixed
outRangeAlpha)
{
size
=
reflectSize(size);
fixed4
color
=
(tex2D(_MainTex,
IN.uv));
fixed
offset
=
max(0,
hueOffset
+
_FlareHuePlus);
fixed3
hueColor;
fixed
outAlpha
=
1;
fixed
inAlpha
=
1;
//渡されたuv座標を作成する円の座標に改変。座標は画面中央を0とし、xもyも最小値が-1、最大値が1になる
fixed
fixX
=
abs(IN.uv.x
-
0.5
-
shiftX)
*
2;
fixed
fixY
=
abs(IN.uv.y
-
0.5
+
shiftY)
*
2;
//解像度の縦横比に関わらず円を正円にするための処理と、円の大きさの修正
fixed
minScreen
=
min(_ScreenParams.x,
_ScreenParams.y);
fixX
=
fixX
*
(minScreen
/
_ScreenParams.y)
/
size;
fixY
=
fixY
*
(minScreen
/
_ScreenParams.x)
/
size;
//画面中央からの直線距離を取得
fixed
atan
=
sqrt(fixX
*
fixX
+
fixY
*
fixY);
//距離からHUE値を取得
fixed
fixHue
=
abs(atan
+
offset);
//HUEをRGBに変換しを虹色として円に反映
hueColor=
(HUEtoRGB((abs(fixHue))
%
1)
/
hueBlendRate);
color.xyz
*=
1
/
hueColor;
//円の中心から外に向かうアルファ値を取得
fixed
fixInStartAlpha
=
inStartAlpha
-
hueBlendRate;
inAlpha
=
saturate(atan
-
fixInStartAlpha);
inAlpha
=
saturate(inAlpha
/
inRangeAlpha);
//円の輪郭付近のアルファ値を取得
fixed
fixOutStartAlpha
=
outStartAlpha
-
(hueBlendRate
/
2)
;
outAlpha
=
saturate(atan
-
fixOutStartAlpha);
outAlpha
=
1
-
saturate(outAlpha
/
outRangeAlpha);
//変数を4つかけて最終的なアルファ値を決定
color.a
=
outAlpha
*
inAlpha
*
overrideRate
*
_FlareLuminance;
return
color;
}
ENDCG
//虹リング大の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.35,
0.35);
fixed4
color
=
createFlare(IN,
0.4,
fixPos.x,
fixPos.y,
0.1,
0.25,
12.8,
0,
0,
0.95,
0.1);
return
color;
}
ENDCG
}
//虹リング小の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.17,
0.17);
fixed4
color
=
createFlare(IN,
0.23,
fixPos.x,
fixPos.y,
0.1,
0.2,
14.9,
0,
1,
0.8,
0.2);
return
color;
}
ENDCG
}
//虹リング中の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.23,
0.23);
fixed4
color
=
createFlare(IN,
0.38,
fixPos.x,
fixPos.y,
0.55,
0.35,
4.65,
0.2,
0.8,
0.9,
0.1);
return
color;
}
ENDCG
}
//白色発光大の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.47,
0.47);
fixed4
color
=
createFlare(IN,
0.6,
fixPos.x,
fixPos.y,
1,
0.9,
0.3,
0,
0,
0.8,
0.7);
return
color;
}
ENDCG
}
//白色発光中の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.26,
0.26);
fixed4
color
=
createFlare(IN,
0.3,
fixPos.x,
fixPos.y,
1,
0.5,
1.5,
0,
0,
0.9,
0.15);
return
color;
}
ENDCG
}
//白色発光小の描写
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed2
fixPos
=
fixFlarePos(0.12,
0.12);
fixed4
color
=
createFlare(IN,
0.15,
fixPos.x,
fixPos.y,
0.9,
0.3,
2,
0,
0,
0.9,
0.1);
return
color;
}
ENDCG
}
//画面全体を発光させる処理
GrabPass{}
Pass
{
CGPROGRAM
fixed4
frag(v2f
IN)
:
SV_Target
{
fixed4
color
=
(tex2D(_GrabTexture,
IN.uv));
color
=
color
*
_Brightness;
color
=
(1
-
_OverrideRate)
*
color
+
(_OverrideRate)
*
_OverrideCol;
return
color
*
_Brightness;
}
ENDCG
}
}
}
プロパティを解説
シェーダーで用意してる9つのパラメーターは次の通り。
_Degree("Degree",
Range(0,
359))
=
90
//光線の角度を指定
_FlareLength("FlareLength",
Range(0,
1))
=
1
//光線の長さを指定
_FlareConverge("FlareConverge",
Range(0,
1))
=
1
//大きいほど光が端に寄り小さいほど中央に寄る
[Space(10)]
_FlareSize("FlareSize",
Range(0.1,
2))
=
1
//レンズフレアの大きさ
_FlareLuminance("FlareLuminance",
Range(0.1,
2))
=
1
//レンズフレアの光の強さ
_FlareHuePlus("FlareHuePlus",
Range(-1,
1))
=
0
//レンズフレアの虹色を変化させる
[Space(10)]
_Brightness("Brightness",
Range(1,
2))
=
1
//画面全体の輝き
_OverrideCol("OverrideCol",
Color)
=
(1.0,
1.0,
1.0,
1.0)
//画面全体を単一色で上書きする際の色
_OverrideRate("OverrideRate",
Range(0,
1))
=
0
//画面全体を単一色で上書きする配合率
以下動画でそれぞれの機能を解説。
Degreeの値を弄ると光線の角度を変えることができる。
FlareLengthの値を弄ると光線の長さを変えることができる。
FlareConvergeの値を弄るとレンズフレアを中央に寄せることができる。
FlareSizeの値を弄るとレンズフレアの大きさを変えることができる。
FlareLuminanceの値を弄るとレンズフレアの輝度を変えることができる。
FlareHuePlusの値を弄るとレンズフレアの虹色を染め具合を変更することが出来る。
Brightnessの値を弄ると画面全体の輝度を変えることができる。
OverrideCol及びOverrideRateの値を弄ると、画面全体を単一色で塗りつぶすことが出来る。
0
0