UnityuGUI用、グレースケール化シェーダー。画像をモノクロ化して灰色に限定。
(Gray Scale Shader for Unity uGUI)
関連ページ
参考URL
色相反転
、
シルエット化
に続いて実装が簡単な部類のグレースケール化シェーダーを作った。
これまでと同じようにBlendRateを作って変化までの割合を調整できるようにしてある。
また世のグレースケール化の計算式には単純平均法とNTSC加重平均法というのがあるらしく、今回
Variant
によってこの2タイプを選べるようにした。
グレースケール化シェーダー、コード全文
プロパティに外出ししてあるBlendRateを弄ることで、徐々にグレースケール化していく。
下がサンプル動画。
下がコード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
Shader
"UI/GrayScale"
{
Properties
{
[KeywordEnum(Simple,
NTSC)]
_GrayType("GrayType",
Int)
=
1
//グレースケール化する際の計算タイプ
_BlendRate
("BlendRate",
Range(0.0,
1.0))
=
1
//完全にグレースケール化するまでの割合
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
#pragma
multi_compile
_GRAYTYPE_SIMPLE
_GRAYTYPE_NTSC
struct
appdata
{
fixed2
uv
:
TEXCOORD0;
fixed4
vertex
:
POSITION;
fixed4
color
:
COLOR;
};
struct
v2f
{
fixed2
uv
:
TEXCOORD0;
fixed4
vertex
:
SV_POSITION;
fixed4
color
:
COLOR;
};
sampler2D
_MainTex;
//プロパティに対応する変数の宣言
fixed
_GrayType;
fixed
_BlendRate;
v2f
vert
(appdata
v)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(v.vertex);
o.uv
=
v.uv;
o.color
=
v.color;
return
o;
}
fixed4
frag
(v2f
i)
:
SV_Target
{
fixed4
col
=
tex2D(_MainTex,
i.uv);
col
*=
i.color;
fixed3
grayCol;
#ifdef
_GRAYTYPE_SIMPLE
//単純平均法で完全なグレースケール化した値を取得
fixed
gray
=
(col.r
+
col.g
+
col.b)
/
3;
grayCol
=
fixed3(gray,
gray,
gray);
#elif
_GRAYTYPE_NTSC
//NTSC加重平均法で完全なグレースケール化した値を取得
fixed
gray
=
dot(col.rgb,
fixed3(0.299,
0.587,
0.114));
grayCol
=
fixed3(gray,
gray,
gray);
#endif
//_BlendRateを参照し、lerpでオリジナル色と完全なグレースケール化の間の値を設定
col.rgb
=
lerp(col.rgb,
grayCol,
_BlendRate);
return
col;
}
ENDCG
}
}
}
プロパティの解説
用意してあるプロパティは2つ。
下はBlendRateが1(完全にグレースケール化した状態)で、単純平均とNTSC加重平均法を切り替えた動画。
テストの画像では、単純平均法の方がやや黒っぽく見える。
コードを少し解説
まず前提としてグレーとは何かと言うと、R(赤)G(緑)B(青)が全て一緒の値のことを言う。
(1, 1, 1)が白、(0, 0, 0)が黒なので、(0.7, 0.7, 0.7)であれば白めの灰色、(0.3, 0.3, 0.3)であれば黒めの灰色になる。
単純平均法とは、対象ピクセルのRGBの値を全て足して、それを3で割ったものでそれぞれのRGB値を上書きする処理のことを言う。
コードで言うと次の部分。
#ifdef
_GRAYTYPE_SIMPLE
//単純平均法で完全なグレースケール化した値を取得
fixed
gray
=
(col.r
+
col.g
+
col.b)
/
3;
grayCol
=
fixed3(gray,
gray,
gray);
例えば対象ピクセルの色が真っ赤(1, 0, 0)だと仮定し、それをこの式に掛けると(0.333.., 0.333.., 0.333..)になる。
対象ピクセルの色が緑(0, 1, 0)でも青(0, 0, 1)でも(0.333.., 0.333.., 0.333..)と同じ値になる。
もし紫(1, 0, 1)なら(0.666.., 0.666.., 0.666..)で、白(1, 1, 1)なら変わらず白(1, 1, 1)である。
これに対しNTSC加重平均法というのは、より人間の目に自然に見えるように、RGBの修正値に適切な比重を加えた計算式になる。
たしかに美術の鉛筆デッサンなどでは、黄色は薄く、赤は濃く塗った方がより自然に見えると教わった。
機械的に平均化しただけでは返って人間の目には違和感が出てしまうと言うことだと思う。
NTSC加重平均法のコードは次の通り。
#elif
_GRAYTYPE_NTSC
//NTSC加重平均法で完全なグレースケール化した値を取得
fixed
gray
=
dot(col.rgb,
fixed3(0.299,
0.587,
0.114));
grayCol
=
fixed3(gray,
gray,
gray);
dotはHLSLの関数で、2つのベクトルの内積を算出する関数となる。
自分のように数学を勉強してこなかった人間からすると、ベクトルや内積と言われるとかなり複雑なことをやってるように見える。
しかしここでやってる事はとてもシンプルになる。
まずNTSC加重平均法のfixed3(0.299, 0.587, 0.114)の数式をよく見てみると、すべて足すと1になるのが分かる。
0.299 + 0.587 + 0.114 = 1
そしてdotの計算式をもっと分かりやすく、その代わり長く書くと次のようなものになる。
#elif
_GRAYTYPE_NTSC
//NTSC加重平均法で完全なグレースケール化した値を取得
//fixed gray = dot(col.rgb, fixed3(0.299, 0.587, 0.114));
fixed
r
=
col.r
*
0.299;
fixed
g
=
col.g
*
0.587;
fixed
b
=
col.b
*
0.114;
fixed
gray
=
r
+
g
+
b;
grayCol
=
fixed3(gray,
gray,
gray);
0.299, 0.587, 0.114は全て足すと1なので、仮に対象ピクセルの色が最大値の白(1, 1, 1)であっても、grayが1を超えることはない。
r = 1 * 0.299 = 0.299
g = 1 * 0.587 = 0.587
b = 1 * 0.114 = 0.114
gray = 0.299 + 0.587 + 0.114 = 1
grayCol = (1, 1, 1)
仮に対象ピクセルの色が赤(1, 0, 0)の場合は次の通りで、最終的にグレースケール化した値は(0.299, 0.299, 0.299)になる。
r = 1 * 0.299 = 0.299
g = 0 * 0.587 = 0
b = 0 * 0.114 = 0
gray = 0.299 + 0 + 0 = 0.299
grayCol = (0.299, 0.299, 0.299)
同じ要領で計算し、緑(0, 1, 0)の場合は(0.587, 0.587, 0.587)、青(0, 0, 1)の場合は(0.114, 0.114, 0.114)となる事が分かる。
これらを纏めると、つまりNTSC加重平均法というのは、赤と青は黒めの灰色と判定し、逆に緑は明るめの灰色と判定してブレンドする数式となる。
0
0