関連ページ
参考URL
趣味や業務内でC,C++,C#,Java,PHPさまざまな言語を扱ってきましたが、これらは結局C言語の文化圏で、ひとつを覚えれば他も大体分かります。
ただシェーダー言語の
HLSL
に初めて触れたときは全く異次元の言語に見えてけっこう絶望しました。
今では少し分かってきてuGUI用シェーダーであれば作れるようになってきました。その解説をしたいと思います。
Unity uGUIは、シェーダー初学者にとってとても良い教材だと自分は思ってます。
※この記事は
旧Unity標準シェーダー(ビルトインシェーダー)の記事
の改訂版で、URPシェーダーに対応したものになります。
2026年2月23日にUnityはURPへの完全移行を発表しました、将来的にはビルトインシェーダーは使えなくなります。

一番シンプルな2Dシェーダー
2Dシェーダーは光の反射を考慮する必要がないし、平面なので3Dシェーダーよりよっぽどシンプルになります。
下のコードは機能を最小限に絞ったuGUI用シェーダーの全文です。
Shader
"Test/Test2DShader"
{
SubShader
{
Tags
{
"Queue"
=
"Transparent"
}
Blend
SrcAlpha
OneMinusSrcAlpha
Pass
{
HLSLPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
// URP用の基本ライブラリをインクルード
#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;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
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;
return
col;
}
ENDHLSL
}
}
}
要点としては、2Dシェーダーでは一番下の関数の
ピクセルシェーダー(フラグメントシェーダー)
しかほぼ弄らないです。
half4 frag (Varyings input) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
col *= input.color;
return col;
}
例えば、関数内の
col *= i.color;
というコードを削除すると、Image ComponentのColorを弄っても全く反映されなくなります。

一行ずつ解説
Shader "Test/Test2DShader"
{
最初の
Shader "Test/Test2DShader"
が、Materialから指定するパスになります。
(詳しくは右の記事参照)
p.52 : シェーダー基礎、Imageにシェーダーを反映させる方法
SubShader
はUnityでシェーダーを作成する際はほぼ必須の行になります。
このブロックの中では、Unityのシステムとシェーダー言語であるHLSLを仲介する様々な機能が使えます。
なおこの仲介役の、HLSL言語をラップした言語を
ShaderLab
と言います。
Tags { "Queue" = "Transparent" }
Tags { "Queue" = "Transparent" }
は描画の実行順で、何も指定しないと"Geometry"になります。
"Geometry"だとuGUIは描画順がおかしくなるので、"Transparent"に決め打ちで良いです。
Cull off
は、
カリング
というポリゴン描画の処理負荷を軽減する機能を無効にする命令です。
一般的に3Dゲームでは、カメラに写ってない裏側のポリゴンはこのカリングで描写を省いてる事が多いです。
uGUIではこのカリングの必要性はないです。
むしろカリングを有効にしてしまうと、Scaleをマイナスにした時に画像が全く描画されなくなります。
uGUIでは、画像を上下反転、もしくは左右反転したい時にマイナスのScale値を使うことがよくあります。
ZWrite Off
は、カメラからの距離によって適切に描画順を管理する機能を無効にする命令です。
通常uGUIでは、z軸の座標は関係なしにHierarchyが下の物ほど手前に表示します。
なのでこの機能はいらないです。
むしろZWriteを有効にしてしまうと、Hierarchyでは下に配置してるのに、何故か手前に表示されない事があります。
Blend SrcAlpha OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
は画像にアルファ値を使う場合必ず必要な行です。これを削除すると下のような謎の画像になります。
Pass
の{}で囲まれた部分がシェーダーの具体的な処理を記述する部分です。
実はピクセルシェーダーの定義以外はPassの外に出すことも出来ます。
その場合すこしコードが増えるので、今回は頂点シェーダーの処理もPass内に突っ込んでいます。
HLSLPROGRAM
はPassとセットになるコードです。
意味としては「ここから先は、GPUが理解できる専用の言語(HLSL)で書くよ!」と言った感じになります。
大体の場合は、Pass内の一番最初にHLSLPROGRAMを記述し、一番最後に
ENDHLSL
を記述します。
#pragma vertex vert
は頂点シェーダーの関数名を宣言しています。
複雑なシェーダー処理の中で唯2つ、
頂点シェーダー
とピクセルシェーダーはプログラマの介入が許されています。
それと同時に、頂点シェーダーとピクセルシェーダーの関数名はプログラマが明示的に宣言し、その処理を定義する必要があります。
頂点
とは、ポリゴンを形成するのに必要な点の数で、平面的な三角形な3つ、四角形なら4つは最低必要になります。
立体的な三角柱なら6つ、四角柱なら8つ最低必要になります。
近年のゲームであれば、1つの3Dモデルで頂点数が10万を超えることも珍しくないらしいです。
uGUIは平面的な四角形として扱われるので、1つの画像につき頂点数は4つになります。
一応Unityの
Window > Analysis > FrameDebugger
から、この頂点数が確認できます。
#pragma fragment frag
はピクセルシェーダーの関数名を宣言しています。別名フラグメントシェーダーとも呼ばれます。
頂点シェーダーは頂点数ごとに呼ばれますが、ピクセルシェーダーは塗りつぶすピクセル数ごとに呼ばれます。
つまり画面上に300 x 300ピクセルの画像を表示する場合、毎フレーム9万回呼ばれることになります。
この塗りつぶすピクセルには、透明色も含まれます。
処理の流れとしては、頂点シェーダーでまず対象モデルの座標や色の情報を編集し、それをピクセルシェーダーに送ります。
情報を受け取ったピクセルシェーダーが更に情報を編集して、画面に出力する感じです。
厳密には頂点シェーダーとピクセルシェーダーの間に、
ラスタライザー
という処理が挟まっています。
uGUIの場合頂点シェーダーでやることはほぼ決まっていて、ここに頭を使う必要はないです。
最後のピクセルシェーダーだけ弄れば大体事足りると思います。
// URP用の基本ライブラリをインクルード
#include
"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
Unityが用意してある様々な便利マクロを使用するために必要な宣言です。
具体的にはコードの後ろに出てくる
TransformObjectToHClip
と
SAMPLE_TEXTURE2D
のマクロを使う際に必要になります。
ここからちょっと話が複雑になるので、複数の題目に分けて解説します。
下は頂点シェーダーとピクセルシェーダーの引数で使う構造体の定義です。
struct Attributes
{
half2 uv : TEXCOORD0;
float4 positionOS : POSITION;
half4 color : COLOR;
};
struct Varyings
{
half2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
half4 color : COLOR;
};
C言語系の構造体と文法がほぼ同じですが、後ろに
: TEXCOORD0
などの変な文字列が入っています。
これは
セマンティクス
と呼ばれるもので後ほど説明します。
Attributes
が入力アセンブラーから頂点シェーダーに送られる構造体の定義になります。これを
入力頂点構造体
と呼びます。
Varyings
がラスタライザからピクセルシェーダーに送られる構造体の定義です。これを
出力頂点構造体
と呼びます。
それぞれの構造体内で、どこからどこまでの情報をシステムから取得するかはプログラマの手に委ねられています。
ここでは
Attributes
と
Varyings
という名前で定義していますが、実際のところはなんでもいいです。
後述する頂点シェーダーとピクセルシェーダーの引数の記述と一致していれば問題がありません。
ちょっとコードを飛ばして頂点シェーダーとピクセルシェーダーの定義を見てみると一致してるのが分かります。
Varyings vert (Attributes input)
{
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float4 frag (Varyings input) : SV_Target
{
構造体の定義内で、half2やhalf4といったC#プログラマには見慣れない宣言が見えます。
half2 uv : TEXCOORD0;
float4 positionOS : POSITION;
half4 color : COLOR;
half
は浮動小数点数を扱う型で、低精度のfloatのことを言います。
floatは32bit、halfは16bitメモリを占有し、
モバイル開発においては、座標計算にはfloat、色やUVにはhalfを使う事が推奨されています。
昔はfixedという更に低精度の浮動小数点数の型もあったのですが、URPでは廃止されました。
厄介なことに、PCのEditor上で開発してるときは常に内部的にはhalfもfloatとして処理されます。
実際の挙動はビルドして確かめる必要があります。
half2
というのは、UnityでいうとVector2の感覚に近いです。配列で内部に2つの浮動小数点数を持っています。
half4
はVector4に近い感覚で、配列で内部に4つの浮動小数点を持っています。
ここでは使ってないですが
half3
もあります。
変数の宣言の後ろに
: TEXCOORD0
や
: POSITION
といった謎の文字列が確認できます。
まずAttributesから見ていきます。
struct Attributes
{
half2 uv : TEXCOORD0;
float4 positionOS : POSITION;
half4 color : COLOR;
};
上で触れた通りこれはセマンティクスと呼ばれるもので、直前のシェーダー処理のステージからどの情報を取得するかを表します。
入力頂点構造体で使うセマンティクスを、
入力セマンティクス
といいます。
: TEXCOORD0;
はその頂点のuv座標です。
UV座標は画像内の座標のことで、x軸もy軸も、0~1の値に正規化されています。
どんなに横長の画像でも、どんなに縦長の画像でも最小値は0、最大値は1となります。
頂点シェーダーは頂点数ごとに呼ばれますが、uGUIは1画像につき4つしか頂点がないです。
つまり四角形の4隅の頂点に対応する、(0, 0), (0, 1), (1, 0), (1, 1)のいずれかの値が渡されることになります。
: POSITION;
はその頂点のワールド座標です。
最終的にゲームプレイヤーが見るのは、ワールド座標ではなくカメラに映りこむ
スクリーン座標
になります。
なのでこの情報のままラスタライザー及びピクセルシェーダーに値を渡しても意味がないです。
プログラマは頂点シェーダー内で明示的にこのワールド座標をスクリーン座標に変換する必要があります。
: COLOR;
はその頂点の色情報です。
3Dモデルであれば、頂点ごとに異なった色情報を格納することが可能になっています。
異なる頂点色は頂点間で
線形補完
され、最終的に画面に映る色に影響を与えます。
uGUIの場合もっとシンプルで、頂点ごとに異なる色情報を持つことは基本的にないです。
4つの頂点すべてに同じカラー値が渡されます。
そのカラー値はどこを参照してるかと言うと、Image Componentの
Color
の部分になります。
次に
Varyings
を見ていきます。
struct Varyings
{
half2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
half4 color : COLOR;
};
出力頂点構造体で使うセマンティクスを、
出力セマンティクス
と言います。
: TEXCOORD0
はそのピクセルのUV座標。
Attributesでは(0, 0), (0, 1), (1, 0), (1, 1)の4パターンしか無かったですが、出力セマンティクスであるVaryingsでは相当多いパターンが考えられます。
ピクセルシェーダーは描画するピクセルごとに呼ばれます。
もし300 x 300pxの画像を描画する場合、x軸を0~1まで300分割、y軸を0~1まで300分割で、9万パターンが想定されます。
例: (0, 0), (0.003, 0), (0.006, 0), (0, 0.003), (0.003, 0.003)など。
: SV_POSITION;
はエンジニアによって加工済みの頂点座標です。
POSITIONと似た意味のセマンティクスで、POSITIONとの違いは、その頂点座標が未加工であるか加工済みであるかの違いになります。
uGUIシェーダーにおいては、頂点シェーダー内でワールド座標からスクリーン座標に書き換えてるのでこちらを使います。
Unity uGUI用シェーダー開発においては、SV_POSITIONをPOSITIONに置き換えても普通に動作することが多いです。
問題が起きないのは、Unityが裏側で気を利かせてSV_POSITIONにコードを自動補完してくれるからです。
しかし一部のモバイルやコンソール機ではエラーとなり表示が真っ黒になってしまうため、正確にSV_POSITIONと入力した方が安心です。
: COLOR;
は対象UV座標の色情報です。
上で述べた通り、この色情報は画像内のピクセルの色ではなくImage Component内での指定値になります。
画像内のピクセル色は、後述するピクセルシェーダー内の
tex2D(_MainTex, i.uv);
で取得しています。
_MainTex
はuGUI Imageがその内部処理で宣言しているテクスチャの変数名です。
TEXTURE2D(_MainTex)
と明示的に宣言することで、ImageのSource Imageにアタッチされた画像を、ピクセルシェーダーで参照できるようになります。
一文字でも違うとバグになるので注意が必要です。
SAMPLER(sampler_MainTex);
_MainTexを参照して、画像をどのように読み込むかという設定情報を、
sampler_MainTex
に代入します。
後ほどのピクセルシェーダー内で使用します。
特別な理由がない限り、名前は必ず sampler + テクスチャ名 にする必要があります。
Varyings vert (Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = input.uv;
output.color = input.color;
return output;
}
頂点シェーダーの実際の処理部分です。
関数名と引数は事前の
#pragma vertex vert
の宣言と、
struct Varyings
の定義通りです。
戻り値の型は、ピクセルシェーダーに渡すために、ピクセルシェーダーの引数と一致させる必要があります。
ここでは事前に定義した
struct Varyings
を当てています。
Varyings output;
でまず変数を用意します。
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
の部分は座標の変換処理です。
対象ピクセルのワールド座標をカメラに投影されるスクリーン座標に置き換えています。
TransformObjectToHClip
はピクセルシェーダーでは機能しないので、座標変換は頂点シェーダー内で行う必要があります。
ここで例えば、変換された座標を少しずらす処理を入れてみます。
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.positionCS = half4(output.positionCS.x + 0.1, output.positionCS.y + 0.1, output.positionCS.z, output.positionCS.w);
すると下の画像のように少し右下にスクリーン上の座標がずれます。
output.uv = input.uv;
では、対象ピクセルのUV座標を代入しています。
例えばこのUV座標を少しずらす処理に置き換えてみます。
//output.uv = input.uv;
output.uv
=
half2(input.uv.x
+
0.25,
input.uv.y);
すると下の画像のように画像内のすべてのピクセル位置が左にずれます。
ずれた分の元画像には存在していないピクセル部分は、元画像の端のピクセルがコピーされ続けます。
output.color = input.color;
でImage ComponentをColorを代入します。
最終的に完成した変数を
rreturn output;
でラスタライザーに渡しています。
half4 frag (Varyings input) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
col *= input.color;
return col;
}
本丸のピクセルシェーダーの処理になります。
関数名と引数は事前の
#pragma fragment frag
の宣言と、
struct Varyings
の定義通りです。
関数の戻り値はピクセルの色を表すRGBAである必要があるので、float4、half4のどちらかを指定します。
この戻り値には
: SV_Target
のセマンティクスが必要になります。
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
で、Source Imageにアタッチされた画像の特定のピクセル色を取得しています。
第一引数は参照する画像、第二引数は画像の読み込み方、第三引数は画像内のどこのピクセル色を取得するかの座標を表しています。
この段階でもUV座標をずらすことは可能で、例えば下のコードに置き換えてみると画像がズレるのが確認できます。
//half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
half4
col
=
SAMPLE_TEXTURE2D(_MainTex,
sampler_MainTex,
half2(input.uv.x
+
0.25,
input.uv.y));
output.color = input.color;
で画像の全てのピクセル色に対し、ImageのColorで設定した値を掛け算で加えています。
シェーダーではRGBAの値は0~255ではなく0~1の範囲で扱います。
例えばアルファを1として、黒なら(0, 0, 0, 1)、青なら(0, 0, 1, 1)、グレーなら(0.5, 0.5, 0.5, 1)になります。
ここでアルファが1とする真っ黄色なピクセル(1, 1, 0, 1)があるとして、Imageのカラー設定がグレー(0.5, 0.5, 0.5, 1)だとします。
この2つの色情報を掛け算すると、それぞれのRGBAの値が掛け算され、(0.5, 0.5, 0, 1)という淀んだ黄色が生まれます。
これがUnity標準のImage Componentのカラー処理になります。
下の画像は、MaterialがアタッチされていないImageのColorを灰色にしたもので、Unity使いなら馴染みある結果だと思います。
return col;
}
ENDHLSL
}
}
}
最後に、編集したピクセルの色情報を返して終了。
ENDHLSL
はすでに説明した
HLSLPROGRAM
と対になるコードです。

色相反転シェーダーを作ってみる
遊びでピクセルシェーダーを弄ってRGBの値を反転するシェーダーを作ってみます。いわゆるネガポジ反転と言われるものです。
単純化するため、ImageのColor値の反映はコメントアウトで無視しています。
half4
frag
(Varyings
input)
:
SV_Target
{
half4
col
=
SAMPLE_TEXTURE2D(_MainTex,
sampler_MainTex,
input.uv);
//col *= input.color;
col.r
=
1
-
col.r;
col.g
=
1
-
col.g;
col.b
=
1
-
col.b;
return
col;
}
下はその結果の画像です。
0
0