導入:なぜシェーダーを学ぶべきか
Godot Engineでゲーム開発を進める上で、 シェーダー は避けて通れない強力なツールです。シェーダーとは、グラフィックス処理ユニット(GPU)上で実行される小さなプログラムのことで、オブジェクトの見た目、色、光の反射、そして画面全体のエフェクトをピクセ ル単位で制御します。
なぜシェーダーが重要なのでしょうか?それは、 視覚的な魅力を飛躍的に向上させる と同時に、 高いパフォーマンス を実現できるからです。CPUで複雑な計算を行う代わりに、GPUの並列処理能力を最大限に活用することで、炎、水、カスタムライティング、ユニークな画面トランジションなど、GDScriptだけでは実現が難しい、あるいは重くなりがちな表現を軽快に実現できます。
本記事では、シェーダーの中でも特に視覚的な表現を担う Fragment Shader(フラグメントシェーダー) に焦点を当て、その基礎知識から、色変更や簡単なエフェクトを作成する最初の一歩を、具体的なコード例とともに解説します。
Godotシェーダーの基本構造
Godot Engineのシェーダーは、[GLSL ES 3.0][1]に似た独自のシェーディング言語を使用します。シェーダーファイル(.gdshader)を作成する際、まずそのシェーダーが何に適用されるかを定義する必要があります。
shader_type canvas_item; // 2Dオブジェクトに適用
// shader_type spatial; // 3Dオブジェクトに適用
canvas_itemは2DスプライトやUI要素に、spatialは3Dメッシュに適用されます。
Godotシェーダーは、主に以下の3つの関数(エントリーポイント)で構成されています。
| 関数名 | 役割 | 実行タイミング |
|---|---|---|
vertex | オブジェクトの頂点(位置)を操作します。変形、波打ち、回転などに使用されます。 | 頂点ごとに1回 |
fragment | ピクセル(フラグメント)の色を計算します。 テクスチャのサンプリング、色調整、エフェクトの核となります。 | ピクセルごとに1回 |
light | オブジェクトに当たる光の影響を計算します。カスタムライティングの実現に使用されます。 | 光源とピクセルの組み合わせごとに1回 |
本記事の主役であるfragment関数は、画面上の すべてのピクセル に対して実行され、そのピクセルの最終的な色を決定する役割を担います。
Fragment Shaderの役割と基本
Fragment Shaderの基本構造は非常にシンプルです。
shader_type canvas_item;
void fragment() {
// ここにピクセルごとの色計算ロジックを記述
}
このfragment関数内で、私たちは主に2つの重要な組み込み変数を使って作業します。
UV(vec2): 現在処理しているピクセルのテクスチャ座標(UV座標)です。値は通常、左上(0.0, 0.0)から右下(1.0, 1.0)の範囲に正規化されています。COLOR(vec4): Fragment Shaderの 最終的な出力色 を格納する変数です。この変数に値を代入することで、ピクセルの色を決定します。
COLORはvec4型で、(R, G, B, A)の4つの浮動小数点数(0.0から1.0の範囲)で構成されます。
実践1: Fragment Shaderで色を変更する
最も基本的なFragment Shaderの使い方は、オブジェクト全体の色を単色に変更することです。
コード例:単色への変更
shader_type canvas_item;
void fragment() {
// COLOR変数に直接、新しい色を代入します。
// vec4(R, G, B, A) の形式で、各成分は0.0から1.0の範囲です。
// 例:鮮やかなマゼンタ(赤と青の最大値、緑はゼロ、不透明度1.0)
COLOR = vec4(1.0, 0.0, 1.0, 1.0); // マゼンタ
}
このシェーダーをスプライトに適用すると、スプライトのテクスチャに関係なく、全体がマゼンタ色に塗りつぶされます。
コード例:テクスチャの色を反転させる
既存のテクスチャの色を活かしつつ、それを操作したい場合は、texture()関数を使って現在のテクスチャの色を取得し、それを基に計算を行います。
shader_type canvas_item;
void fragment() {
// 1. 現在のUV座標にあるテクスチャの色を取得します。
vec4 texture_color = texture(TEXTURE, UV);
// 2. 色を反転させます。(1.0から元の色を引く)
// アルファ値(透明度)はそのまま維持します。
vec3 inverted_rgb = vec3(1.0) - texture_color.rgb;
// 3. 最終的な出力色COLORに設定します。
COLOR = vec4(inverted_rgb, texture_color.a);
}
このように、Fragment Shaderはピクセルごとにテクスチャの色を読み取り、計算を施し、新しい色を書き出すという処理を高速に行います。
実践2: UV座標と時間を使ったエフェクト
Fragment Shaderの真価は、 UV 座標や組み込みの TIME 変数といった入力値を使って、動的なエフェクトを作成できる点にあります。
コード例:シンプルなグラデーション
UV座標のx成分(横方向)を使って、左から右へ黒から白へのグラデーションを作成してみましょう。
shader_type canvas_item;
void fragment() {
// UV.xは左端で0.0、右端で1.0の値を取ります。
float gradient = UV.x;
// この値をR, G, Bの各成分に設定します。
// 例:グレースケールのグラデーション
COLOR = vec4(gradient, gradient, gradient, 1.0);
}
コード例:UV座標を使った円形マスク
UV 座標の中心からの距離を利用して、円形のマスクエフェクトを作成してみましょう。これは、スポットライトや爆発の波紋など、様々なエフェクトの基礎となります。
shader_type canvas_item;
void fragment() {
// 1. UV座標を中央(0.5, 0.5)が原点となるように変換します。
vec2 centered_uv = UV - vec2(0.5);
// 2. 原点からの距離を計算します。(length関数を使用)
float distance = length(centered_uv);
// 3. 距離に基づいてアルファ値(透明度)を決定します。
// smoothstep関数を使って、0.3から0.4の距離で滑らかに透明になるようにします。
float alpha = 1.0 - smoothstep(0.3, 0.4, distance);
// 4. 元のテクスチャの色を取得します。
vec4 texture_color = texture(TEXTURE, UV);
// 5. 最終的な出力色に、計算したアルファ値を適用します。
COLOR = vec4(texture_color.rgb, texture_color.a * alpha);
}