[Unity uGUI] グレースケール化シェーダー(Gray Scale Shader)。
画像のモノクロ化、単純平均法とNTSC加重平均法の仕組みの解説。
関連ページ
参考URL
色相反転
、
シルエット化
に続いて実装が簡単な部類のグレースケール化シェーダーを作りました。
これまでと同じように
BlendRate
を作って変化までの割合を調整できるようにしてあります。
また世のグレースケール化の計算式には
単純平均法
と
NTSC加重平均法
というのがあるらしく、今回
Variant
によってこの2タイプを選べるようにしました。
※コードが古くなっていたのでURP用に記事を改修しました(2026/03/22)。

グレースケール化シェーダー概要
プロパティに外出ししてあるBlendRateを弄ることで、徐々にグレースケール化していきます。
下がサンプル動画。

コード全文
以下グレースケール化シェーダーの全文です。uGUI Imageへの反映方法は
こちらの記事
参照してください。
基礎知識として次の記事にも目を通すと分かり易いです。
p.57 : シェーダー基礎、最もシンプルなuGUI用 URP Shader

Shader
"UI/GrayScale"
{
Properties
{
[KeywordEnum(Simple,
NTSC)]
_GrayType("GrayType",
Int)
=
1
//グレースケール化する際の計算タイプ
_BlendRate
("BlendRate",
Range(0.0,
1.0))
=
1
//完全にグレースケール化するまでの割合
}
SubShader
{
Tags
{
"RenderPipeline"
=
"UniversalPipeline"
"Queue"
=
"Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
Pass
{
HLSLPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
#pragma
multi_compile
_GRAYTYPE_SIMPLE
_GRAYTYPE_NTSC
#include
"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct
Attributes
{
half2
uv
:
TEXCOORD0;
float4
positionOS
:
POSITION;
half4
color
:
COLOR;
};
struct
Varyings
{
half2
uv
:
TEXCOORD0;
float4
positionCS
:
SV_POSITION;
half4
color
:
COLOR;
};
//uGUI Image > Source Imageにアタッチされた画像を参照する
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
//SRP Batcherを適用
CBUFFER_START(UnityPerMaterial)
//プロパティに対応する変数の宣言
half
_GrayType;
half
_BlendRate;
CBUFFER_END
Varyings
vert
(Attributes
input)
{
Varyings
output;
output.positionCS
=
TransformObjectToHClip(input.positionOS.xyz);
output.uv
=
input.uv;
output.color
=
input.color;
return
output;
}
half4
frag
(Varyings
input)
:
SV_Target
{
half4
col
=
SAMPLE_TEXTURE2D(_MainTex,
sampler_MainTex,
input.uv);
col
*=
input.color;
#if
defined(_GRAYTYPE_SIMPLE)
//単純平均法で完全なグレースケール化した値を取得
half
gray
=
(col.r
+
col.g
+
col.b)
/
3;
half3
grayCol
=
half3(gray,
gray,
gray);
#elif
defined(_GRAYTYPE_NTSC)
//NTSC加重平均法で完全なグレースケール化した値を取得
half
gray
=
dot(col.rgb,
half3(0.299,
0.587,
0.114));
half3
grayCol
=
half3(gray,
gray,
gray);
#endif
//_BlendRateを参照し、lerpでオリジナル色と完全なグレースケール化の間の値を設定
col.rgb
=
lerp(col.rgb,
grayCol,
_BlendRate);
return
col;
}
ENDHLSL
}
}
}


プロパティ解説
用意してあるプロパティは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