2Dゲームを作っていて、「敵を倒したときに一瞬だけ白く光らせたい」「水面をゆらゆら流したい」「敵をじわっと溶けるように消したい」——そんな表現を作ろうとして、modulate やアニメーションだけでは手が届かず悩んだことはないでしょうか。
こうした「ピクセル単位のこだわった見た目」を実現するのが シェーダー (Shader) です。シェーダーは GPU(グラフィックス処理ユニット)の上で動く小さなプログラム で、画面に映る1ピクセル1ピクセルの色を、あなたのコードで直接決められます。この記事では、その中でも色を担当する Fragment Shader(フラグメントシェーダー) に絞って、「最初の一歩」を踏み出せるところまで解説します。
この記事でわかること
- シェーダーとは何か ——なぜGPUだと重い見た目でも軽く動くのか
- Godotシェーダーの 基本構造(
shader_typeとvertex/fragment/light)fragment関数でCOLOR・UV・TEXTUREを使い、色を操作するUVとTIMEで作る 動的エフェクト(円形マスク・スクロール・ディゾルブ)if文を避けるなど、 パフォーマンスの勘どころ
動作確認環境: Godot 4.7
シェーダーとは? 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パターンを見てみましょう。
