Unity uGUI用モザイクシェーダー。モザイクの仕組みも解説。
(Mosaic Shader for Unity uGUI)
関連ページ
モザイクシェーダーなんてググればすぐヒットするんで敢えて自分が公開する必要もないけど、メモ書きのためにも公開。
一応
Variant
を使って、粗いモザイクと精度の高いモザイクを選択できるようにした。
粗いモザイクもなかなか味があるのでお好みで。
モザイクシェーダー、コード全文
次の2つの動画がモザイクシェーダーの演出効果。1つ目が粗い精度、2つ目が細かい精度。
2つの処理の違いは、省略するピクセルの色情報を全て破棄するか、省略するピクセルの色の平均色を出力するかの違い。
以下コード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
今回の場合GrabPassを使っているので、
こちら
の知識も必要になる。
Shader
"UI/GrabMosaic"
{
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
[KeywordEnum(one,
equalize)]
_Pick("ColorPick",
Int)
=
1
_MosaicNum
("Mosaic
Num",
Range(0.0,
50.0))
=
0
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
"RenderType"="Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
GrabPass{}
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
#pragma
multi_compile
_PICK_ONE
_PICK_EQUALIZE
#include
"UnityCG.cginc"
sampler2D
_GrabTexture;
float4
_GrabTexture_TexelSize;
float
_MosaicNum;
struct
appdata
{
fixed4
vertex
:
POSITION;
fixed4
uv
:
TEXCOORD0;
};
struct
v2f
{
fixed4
vertex
:
SV_POSITION;
fixed2
uv
:
TEXCOORD0;
};
v2f
vert(appdata
i)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(i.vertex);
o.uv
=
ComputeGrabScreenPos(o.vertex);
return
o;
}
//UV座標を大雑把にして、複数の隣り合うpixelのcolor値を同一色にする
fixed2
get_mosaic_uv(fixed2
uv,
fixed
fixMosaicNum)
{
fixed2
mosaicUV
=
floor(uv
/
fixMosaicNum)
*
fixMosaicNum;
#ifdef
_PICK_ONE
//座標の取得を大雑把にした結果画像が少し右下にずれる予定なので、左上に少しずらして修正
return
mosaicUV+
fixMosaicNum
/
2;
#elif
_PICK_EQUALIZE
//座標の取得を大雑把にした結果画像が少し下にずれる予定なので、上に少しずらして修正
return
fixed2(mosaicUV.x,
mosaicUV.y
+
fixMosaicNum);
#endif
}
//渡したUV座標からGrabした画像のcolorを抜き出す
fixed4
get_tex_color(fixed2
uv,
fixed
fixMosaicNum)
{
#ifdef
_PICK_ONE
//指定したuv座標のcolorをそのまま返す
return
tex2D(_GrabTexture,
uv);
#elif
_PICK_EQUALIZE
//指定したuv座標から、省略する予定のpixel全体を平均化したcolor値を返す
fixed4
col;
int
count;
for(int
jx
=
0;
jx
<=
floor(_GrabTexture_TexelSize.z
*
fixMosaicNum);
jx++)
{
for(int
jy
=
0;
jy
<=
floor(_GrabTexture_TexelSize.w
*
fixMosaicNum);
jy++)
{
fixed
pickX
=
jx
*
_GrabTexture_TexelSize.x;
fixed
pickY
=
jy
*
_GrabTexture_TexelSize.y;
col
=
col
+
tex2D(_GrabTexture,
uv
+
fixed2(pickX,
pickY));
count++;
}
}
return
col
/count;
#endif
}
fixed4
frag(v2f
i)
:
SV_Target
{
//もし_MosaicNumが0より大きいなら1を返し、それ以外は0を返す
//shaderではif文が推奨されてないので、if文の代わりとして使う
fixed
isUseMosaic
=
1
-
step(_MosaicNum,
0);
//入力された_MosaicNumの値を、実際にモザイク処理をするのに使う値に修正
fixed
fixMosaicNum
=
_MosaicNum
*
0.001f;
//大雑把にしたUV座標。
fixed2
mosaicUV
=
get_mosaic_uv(i.uv,
fixMosaicNum);
//元々のUV座標から摘出したcolor値。ただしisUserMosaicが1なら0が代入される。
fixed4
originalCol
=
(1
-
isUseMosaic)
*
tex2D(_GrabTexture,
i.uv);
//大雑把にしたUV座標から摘出したcolor値。ただしisUserMosaicが0なら0が代入される。
fixed4
mosaicCol
=
isUseMosaic
*
get_tex_color(mosaicUV,
fixMosaicNum);
//colorの値を大きい方選択して返す
return
max(originalCol,
mosaicCol);
}
ENDCG
}
}
}
プロパティを解説
用意してあるプロパティは2つのみ。
モザイクの仕組みについて
モザイクシェーダーは一見複雑なことをやっていそうだけど、理屈はとてもシンプルになっている。
まずよりシンプルなColorPick.oneでの挙動を解説していく。
ColorPick.oneの処理を端的に言うと、UV座標を大雑把にして取得するピクセル色をずらしているだけになる。
シェーダーに渡されるUV座標は、左下が(x=0, y=0)、右上が(x=1, y=1)となっている。
例えば縦も横も6ピクセルの下のような画像をシェーダーに渡したとすると、UV座標は次の通り。
6x6ピクセルは極端に小さい画像なので、UV座標は0.2ずつ移動する。
上の画像では、x軸が0から1に進むごとに色が赤色から黄色に変化している。
また黒いグリッド線が表示されているが、これは解説のためであり実際はないものとする。
さて、ここで渡されたUV座標のx軸に対して、次のような計算式を当てて改変したとする。
floorはHLSL言語の関数で、小数点以下切り捨てを意味する。
この計算を実行すると、0.2は0に、0.4は元と同じ0.4になる。
この調子で全てのx軸に改変を加えると下のような図になる。
改変されたx座標を元に、取得するピクセル色が少しずつずれている。
これがColorPick.oneの概ねのモザイク処理になる。
実際はx軸だけでなくy軸に対してもこの改変処理を行っている。
またここでは係数として0.4を使っているか、この値を大きくすればするほどモザイクもより大雑把になる。
ColorPick.equalizeの方は、省略する座標のピクセル色を捨て去るのではなく、平均色を出力させる。
黄色から赤のグラデーションだと今一違いが分かりづらいので、下のようなパステル調のRGB色の画像を用意。
この画像に対し、係数を0.4としてx軸にColorPick.oneの処理を掛けると次のようになる。
一方でColorPick.equalizeを選択すると次の通り。
パステル赤とパステル緑が混じってややくすんだ黄色に。パステル青とパステル赤が混じってパステル紫に。
そしてパステル緑とパステル青が混じってパステル青緑になっている。
1
1