[Unity uGUI] 色反転シェーダー(Invert Color Shader)。
URPで色が白浮きする理由、リニア空間とガンマ空間について解説。
関連ページ
たぶんシェーダーで作るのが簡単な部類の色反転シェーダー。写真業界ではネガポジ反転とか言ったりするらしいです。
ShaderGraphだとInvertColorという名前のノードがデフォルトで搭載されています。
単純に反転するだけだとやや簡単なので、BlendRateという外出しのプロパティを用意して、その値に応じて反転率を調整できるようにしました。
※コードが古くなっていたのでURP用に記事を改修しました(2026/03/20)。
URP特有の問題となるリニア空間とガンマ空間の問題についても解説を追記しました。

色反転シェーダー概要
マテリアルの
BlendRate
のスライダーで、反転率を0から1までのfloat値で指定できます。
今回URP用にコードを改修したのですが、不思議だったのはビルトインシェーダー(旧Unity標準シェーダー)に比べて、画像が白浮きすることでした。
左はビルトインで作ったシェーダー、右はURPで作ったシェーダーです。
最終的にはURP環境でも白浮きを修正できたので、それも含めたコードを紹介します。

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

Shader
"UI/InvertColor"
{
//Propertiesに_BlendRateを指定してInspectorからアクセスできるようにする
Properties
{
_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
#include
"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
//後述のLinearToSRGB、SRGBToLinearで必要なライブラリ
#include
"Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.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
_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;
//ピクセル色をリニア空間ルールから一旦ガンマ空間ルールへ変換
half3
sRGB
=
LinearToSRGB(col.rgb);
//ガンマ空間ルール上で色を反転
sRGB
=
abs(_BlendRate
-
sRGB);
//色をガンマ空間ルールからリニア空間ルールに戻す
col.rgb
=
SRGBToLinear(sRGB);
return
col;
}
ENDHLSL
}
}
}


コード解説
本ピクセルシェーダー内ではリニア空間、ガンマ空間の変換処理もやっていますが、一旦それは置いて最初に色反転の仕組みを解説します。
まずshaderで色は0~255の値ではなく0~1のfloat値で管理しています。
赤なら(1, 0, 0)、青なら(0, 0, 1)、紫なら(1, 0, 1)といった具合です。なのでそれぞれのピクセルのRGBを1から引けば完全な色反転になります。
赤の反転色は(0, 1, 1)で水色に、青の反転色は(1, 1, 0)で黄色になります。
BlendRate
を使用しないシンプルな色反転の場合、ピクセルシェーダーのコードは次の通りです。
今回のシェーダーの場合は1という固定値ではなく、ユーザーが指定する
BlendRate
を参照します。
ただ1より低い値からRGBの値を引くと、結果がマイナスになってしまう可能性があります。
なので
abs
使う事によって絶対値にして、マイナスの値は出力しないようにしています。
col.rgb = abs(_BlendRate - col.rgb);
実際のピクセルシェーダーの中身では、色反転処理以外にもリニア空間やらガンマ空間といったややこしい事をやっています。
half4
frag
(Varyings
input)
:
SV_Target
{
half4
col
=
SAMPLE_TEXTURE2D(_MainTex,
sampler_MainTex,
input.uv);
col
*=
input.color;
//ピクセル色をリニア空間ルールから一旦ガンマ空間ルールへ変換
half3
sRGB
=
LinearToSRGB(col.rgb);
//ガンマ空間ルール上で色を反転
sRGB
=
abs(_BlendRate
-
sRGB);
//色をガンマ空間ルールからリニア空間ルールに戻す
col.rgb
=
SRGBToLinear(sRGB);
return
col;
}
簡単に言うと、リニア空間は数学的に正しい明るさの設定、ガンマ空間は人間の目には正しく見える明るさの設定です。
人間は「暗い部分の変化」にとても敏感で、「明るい部分の変化」には鈍感です。
そのため画像データなどは、暗い部分にたくさんの情報(階調)を割り当てて保存しています。これが「ガンマ空間」です。
私たちが普段見ている「中間のグレー」は、ガンマ空間の数値で言うと0.5くらいです
。
コンピュータ(URPのリニア設定など)は、光の物理的な強さをそのまま数値として計算します。
ガンマ空間で言う0.5は、リニア空間では0.2に相当します。
つまりURPのリニア設定のまま色反転させてしまうと、人間の目にはかなり明るく見えてしまうという事です。
見た目の中間(0.2相当)を反転させた結果(0.8相当)が、本来の半分(0.5)よりずっと明るい値になってしまうのが白浮きが激しくなります。
このピクセルシェーダーでは、一旦リニア空間をガンマ空間に変換し、ガンマ空間ルール上で色反転して、最後にまたリニア空間に戻しています。
//ピクセル色をリニア空間ルールから一旦ガンマ空間ルールへ変換
half3
sRGB
=
LinearToSRGB(col.rgb);
//ガンマ空間ルール上で色を反転
sRGB
=
abs(_BlendRate
-
sRGB);
//色をガンマ空間ルールからリニア空間ルールに戻す
col.rgb
=
SRGBToLinear(sRGB);
またこの処理で使っている関数
LinearToSRGB
及び
SRGBToLinear
を使うために、上の方で以下のライブラリを読み込んでいます。
//後述のLinearToSRGB、SRGBToLinearで必要なライブラリ
#include
"Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
0
0