2Dゲームを作っていて、「敵を倒 したときに一瞬だけ白く光らせたい」「水面をゆらゆら流したい」「敵をじわっと溶けるように消したい」——そんな表現を作ろうとして、modulate やアニメーションだけでは手が届かず悩んだことはないでしょうか。
こうした「ピクセル単位のこだわった見た目」を実現するのが シェーダー (Shader) です。シェーダーは GPU(グラフィックス処理ユニット)の上で動く小さなプログラム で、画面に映る1ピクセル1ピクセルの色を、あなたのコードで直接決められます。この記事では、その中でも色を担当する Fragment Shader(フラグメントシェーダー) に絞って、「最初の一歩」を踏み出せるところまで解説します。
この記事でわかること
- シェーダーとは何か ——なぜGPUだと重い見た目でも軽く動くのか
- Godotシェーダーの 基本構造(
shader_typeとvertex/fragment/light)fragment関数でCOLOR・UV・TEXTUREを使い、色を操作するUVとTIMEで作る 動的エフェクト(円形マスク・スクロール・ディゾルブ)if文を避けるなど、 パフォーマンスの勘どころ
シェーダーとは? CPUとGPUの絵の描き方の違い
シェーダーを理解する第一歩は、 「絵を描いているのは誰か」 を知ることです。ゲームの画面は、毎フレーム何十万個ものピクセルに色を塗って作られています。この塗り作業を担当するのが GPU です。
普段GDScriptで書くゲームのロジック(移動・当たり判定・スコア計算など)は CPU が処理します。CPUは1つの仕事を順番にこなすのが得意です。一方GPUは、 たくさんの単純な作業を一斉に片付ける のが得意——だから こそ、何十万ピクセルもの塗り作業を一瞬で終わらせられます。

シェーダーとは、この 「GPUに、ピクセルの色の塗り方を教える小さなプログラム」 のことです。GPUの並列処理に乗るので、炎・水・歪み・画面全体のトランジションといった重たそうな表現も、ゲーム本体のロジックを邪魔せず軽快に動きます。
modulateとの違い ——「一律」か「ピクセルごと」か
「色を変えるだけなら、modulate プロパティでもできるのでは?」と思うかもしれません。ここが重要な分かれ目です。
modulate:オブジェクト 全体を一律に 同じ色で染める。「キャラ全体を赤くする」のようなシンプルな用途向き。- シェーダー :ピクセル 1つ1つに別々の計算 ができる。「縁だけ光らせる」「上半分だけ透明にする」「中心から放射状にグラデをかける」など、 位置によって効果が変わる 表現が作れる。
つまり「一律の色変え」なら modulate、「位置ごとに変わる凝った表現」ならシェーダー、という住み分けです。単純な点滅だけなら modulateによるホワイトフラッシュ の方が手軽なこともあり ます。
Godotシェーダーの基本構造
Godotのシェーダーは、GLSL ES 3.0に似た独自の言語で書きます。シェーダーは .gdshader ファイル(またはノードの ShaderMaterial 内)に記述し、まず 「何に適用するか」 を1行目で宣言します。
shader_type canvas_item; // 2D(スプライトやUIなどCanvasItem系)に適用
// shader_type spatial; // 3Dメッシュに適用する場合はこちら
2Dのエフェクトを作るときは canvas_item を指定します。そのうえで、シェーダーは主に次の3つの 関数(エントリーポイント) で構成されます。
| 関数名 | 役割 | 実行タイミング |
|---|---|---|
vertex | 頂点(位置)を操作する。変形・波打ち・回転など | 頂点ごとに1回 |
fragment | ピクセル(フラグメント)の色を計算する。 色調整・エフェクトの核 | ピクセルごとに1回 |
light | オブジェクトに当たる光の影響を計算する。カスタムライティング | 光源×ピクセルごとに1回 |

この記事の主役は fragment 関数 です。画面に映る すべてのピクセル に対して1回ずつ実行され、そのピクセルの最終的な色を決めます。まずはここだけ押さえれば、色変更やエフェクトの大半が作れます。
fragment関数で色を決める - COLORとUV
fragment 関数の仕事は、 「今、塗ろうとしているこのピクセルを何色にするか」 を決めることです。そのために2つの重要な組み込み変数を使います。
COLOR:出力。ここにvec4(R, G, B, A)形式で色を代入すると、そのピクセルがその色になる(各成分は0.0〜1.0)UV:入力。 今処理しているピクセルが、テクスチャ上のどの位置か を0.0〜1.0の座標で表す
UV は、テクスチャの左上を (0, 0)、右下を (1, 1) とする座標です。ピクセルごとに違う UV が渡されるので、「左半 分だけ」「中心からの距離に応じて」といった 位置に応じた処理 の基準になります。

コード例1:単色で塗りつぶす
もっともシンプルな、全ピクセルを同じ色にするシェーダーです。
shader_type canvas_item;
void fragment() {
// COLORに色を直接代入。vec4(R, G, B, A)、各成分は0.0〜1.0
COLOR = vec4(0.8, 0.2, 0.3, 1.0); // 不透明のワインレッド
}
コード例2:テクスチャの色を反転する
元の絵を活かして加工してみましょう。texture() 関数で、今のピクセルの元の色を取り出せます。
shader_type canvas_item;
void fragment() {
vec4 tex = texture(TEXTURE, UV); // このピクセルの元の色を取得
vec3 inverted = vec3(1.0) - tex.rgb; // RGBを反転(1.0から引く)
COLOR = vec4(inverted, tex.a); // アルファ(透明度)は元のまま
}
texture(TEXTURE, UV) が「このスプライトの、この位置の色を読む」の基本形です。透明な部分(tex.a が 0)まで色を変えてしまわないよう、 アルファは元の値を引き継ぐ のがポイントです。
コード例3:セピア調フィルター
もう少し実践的に、写真をセピア調にするフィルター です。まず明るさ(輝度)を求め、そこにセピアの色味を掛けます。
shader_type canvas_item;
void fragment() {
vec4 tex = texture(TEXTURE, UV);
float gray = dot(tex.rgb, vec3(0.299, 0.587, 0.114)); // 輝度に変換
vec3 sepia = vec3(gray * 1.07, gray * 0.74, gray * 0.43); // セピアの色味
COLOR = vec4(sepia, tex.a);
}
同じ「色を読んで加工して書き戻す」流れで、グレースケール化・色調補正・2値化など、画面のトーンを変えるフィルターが一通り作れます。
UVとTIMEで動かす - 動的エフェクト
Fragment Shaderの真価は、UV に加えて TIME(起動からの経過秒数) を使い、静止画を 動くエフェクト に変えられる点にあります。定番の3パターンを見てみましょう。

コード例4:円形マスク(スポットライト)
UV から中心までの距離を測り、外側を透明にすると、スポットライトのような円形マスクになります。
shader_type canvas_item;
void fragment() {
vec2 centered = UV - vec2(0.5); // 中心を(0,0)に補正
float dist = length(centered); // 中心からの距離
// 半径0.3〜0.4の間でなめらかに透明へ(境界をぼかす)
float mask = 1.0 - smoothstep(0.3, 0.4, dist);
vec4 tex = texture(TEXTURE, UV);
COLOR = vec4(tex.rgb, tex.a * mask); // 元のアルファにマスクを掛ける
}
smoothstep() は、境界をカクッとさせず なめらかに 切り替える関数です。後述しますが、if で「距離が0.4以上なら透明」と書くよりGPUに優しい書き方でもあります。
コード例5:時間でスクロールする
UV に TIME を足すと、テクスチャが自動でスクロールします。流れる水面・雲・ベルトコンベアなどに使えます。
shader_type canvas_item;
uniform float scroll_speed = 0.1; // GDScriptから速度を渡せる
void fragment() {
vec2 uv = UV + vec2(TIME * scroll_speed, 0.0); // X方向へずらす
uv = fract(uv); // 0.0〜1.0にループさせる(無限スクロール)
COLOR = texture(TEXTURE, uv);
}
fract()(小数部を取り出す関数)でUVを 0.0〜1.0 に折り返すことで、テクスチャが途切れず無限に流れ続けます。
コード例6:ノイズで溶けるディゾルブ
ノイズテクスチャを使うと、キャラが 有機的に溶けて消える ディゾルブが作れます。撃破演出やワープに定番です。
shader_type canvas_item;
uniform sampler2D noise_texture; // ノイズ画像を外部から渡す
uniform float dissolve : hint_range(0.0, 1.0) = 0.0; // 進行度(0=通常, 1=消滅)
void fragment() {
float noise = texture(noise_texture, UV).r; // ノイズの濃さ(0〜1)
if (noise < dissolve) {
discard; // このピクセルを描かない(消す)
}
COLOR = texture(TEXTURE, UV);
}
GDScriptから dissolve を 0.0 → 1.0 へ Tween で変化させれば、ノイズの薄いところから順に穴が空いて、じわっと溶けるアニメーションになります。
実践:被弾で一瞬白く光る「ヒットフラッシュ」を作る
ここまでの「色を読んで加工する」を、実際のゲームで最もよく使う演出に落とし込んでみましょう。 敵が攻撃を受けた瞬間に一瞬だけ真っ白に光る「ヒットフラッシュ」 です。
シューティングの敵編隊、アクションRPGのボス、ベルトスクロールの雑魚——ジャンルを問わず「当たった感触」を出す定番演出で、しかも 1つのシェーダーを全キャラで使い回せる のが嬉しいところです。
