shader-programming について
GPU の頂点シェーダーやフラグメントシェーダーを学び、実装する開発者向けのクロスエンジン型スキルです。GLSL の汎用的な基礎を HLSL の対応例とともに解説し、一般的な視覚効果や注意点も扱います。
主な機能
- 01頂点・フラグメントシェーダーの基礎
- 02GLSL と HLSL の概念対応
- 03UV と座標空間のガイド
- 04一般的なエフェクトの実装例
- 05性能とプラットフォームの注意点
活用シーン
- 01スクロールテクスチャのシェーダー作成
- 02ディゾルブやリムライトの効果作成
- 03エンジン間でのシェーダー概念の変換
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill shader-programmingインストール後、Claude Code で/shader-programmingを入力して読み込まれたか確認します。
同梱ファイルreferences/effects.md
詳細
シェーダープログラミング(エンジン横断)
シェーダーは、GPU 上で頂点ごとおよびピクセルごとに実行される小さなプログラムです。
パイプライン、座標空間、UV、一般的なエフェクトの構築方法といった概念はエンジン間で共通です。変わるのは言語の方言と組み込み変数名だけです。このスキルでは、GLSL と HLSL の同等の記述を使って、応用可能な基本を学びます。エンジン固有の正確な構文や組み込み機能については、godot-shaders(または Unity/Unreal のマテリアルドキュメント)を使ってください。
使用する場面
- 頂点シェーダーやフラグメントシェーダーを理解または記述したり、UV、座標空間、GPU パイプラインについて考えたりする場合に使用します。
- 色調整/再配色、テクスチャのスクロール、ディゾルブ、アウトライン、フレネル/リムライト、ビネット、カラーグレーディングなどの一般的なエフェクトを作成する場合に使用します。
- GLSL と HLSL の間、またはエンジン間でシェーダーの概念を置き換える場合に使用します。
使用しない場面: エンジン固有の正確なシェーダー言語や組み込み機能については、godot-shaders(Godot シェーディング言語)またはエンジンのマテリアルドキュメントを使用してください。パーティクル VFX システム全体については、unreal-niagaraを参照してください。ポストプロセスのスタックについては、エンジンのレンダラー設定を参照してください。
基本的なワークフロー
- どのステージにいるかを把握します。 頂点シェーダーは各頂点をクリップ空間に変換し、データ(UV、法線)を後続のステージに渡します。フラグメント/ピクセルシェーダーはラスタライズされたピクセルごとに実行され、色を出力します。ゲームエフェクトのほとんどはフラグメントステージで処理されます。
- 座標空間を追跡します。 位置はモデル空間 → ワールド空間 → ビュー空間 → クリップ空間へと移動します。法線はワールド空間またはビュー空間に属します。座標空間を混在させることが、最もよくあるバグです。
- UV と時間でエフェクトを制御します。 UV は
0..1のテクスチャ座標です。オフセット、スケーリング、歪みを加え、timeユニフォームでアニメーションさせます。 - ピクセルごとに処理し、分岐を抑えます。 可能であれば
ifよりもmix、step、smoothstep、clampを優先します。GPU はピクセルをロックステップで処理するため、分岐の不一致を嫌います。 - データはユニフォーム(ドローごとに一定)とバリアリング(頂点からフラグメントへ補間)で渡します。テクスチャサンプルは少なくしてください。サンプルがコストの大部分を占めます。
- ツールがすでに利用可能な場合は、視覚テストの前にソースを検証します。 GLSL では、対応するステージの拡張子を使って
glslangValidator path/to/shader.fragを実行します。WGSL では、naga path/to/shader.wgslを実行します。Godot の.gdshaderファイルは、単体のバリデーターがないため、プロジェクトを Godot で開いて確認します。 - 実際の対象ハードウェアで視覚的に検証します。 デスクトップで正しく見えるシェーダーでも、モバイルでは問題が起きることがあります(精度、機能の欠如など)。実際に出荷する環境でテストしてください。
パターン
GLSL 形式のフラグメントスニペット(Godot のcanvas_item/spatialシェーダーや OpenGL に近い形式)です。HLSL 形式の同等の記述と、アウトライン/フレネル/ビネットのシェーダー全体については、references/effects.mdを参照してください。
1. フラグメントの基本:サンプリング、色調整、合成
// Per-pixel: read the texture at this UV, multiply by a color (tint), keep alpha.
uniform sampler2D tex;
uniform vec4 tint; // e.g. (1,0,0,1) reddens; multiply is non-destructive
in vec2 uv; // interpolated 0..1 texture coordinate (a "varying")
out vec4 frag;
void main() {
vec4 c = texture(tex, uv); // HLSL: tex.Sample(samp, uv)
frag = c * tint; // component-wise multiply tints without clipping
}
2. UV のスクロール(アニメーションテクスチャ)— フレームレート非依存
// Add time * speed to the UV to scroll. fract() wraps it into 0..1 so it tiles.
uniform sampler2D tex;
uniform float time; // seconds, supplied by the engine
uniform vec2 scroll_speed; // UV units per second, e.g. (0.1, 0.0)
in vec2 uv;
out vec4 frag;
void main() {
vec2 scrolled = fract(uv + scroll_speed * time); // HLSL: frac(...)
frag = texture(tex, scrolled);
}
// Drive with a real time uniform, not a per-frame accumulator, so speed is stable.
3. ディゾルブ(ノイズマップをしきい値処理し、エッジを発光させる)
// Hide pixels where noise < threshold; tint a thin band at the boundary.
uniform sampler2D tex;
uniform sampler2D noise_tex; // grayscale noise, 0..1
uniform float amount; // 0 = fully visible, 1 = fully dissolved
uniform float edge = 0.05; // width of the glowing edge band
uniform vec4 edge_color;
in vec2 uv;
out vec4 frag;
void main() {
vec4 c = texture(tex, uv);
float n = texture(noise_tex, uv).r;
if (n < amount) discard; // cut away dissolved pixels
float e = smoothstep(amount, amount + edge, n); // 0 at the edge -> 1 inside
frag = mix(edge_color, c, e); // HLSL: lerp(edge_color, c, e)
}
4. フレネルリムライト(3D)— 斜めの角度を明るくする
// Rim = 1 where the surface faces away from the camera (silhouette glow).
in vec3 world_normal; // normalized, world space (from the vertex stage)
in vec3 view_dir; // normalized, surface -> camera, world space
uniform float power = 3.0;
uniform vec3 rim_color;
out vec4 frag;
void main() {
float f = pow(1.0 - clamp(dot(world_normal, view_dir), 0.0, 1.0), power);
frag = vec4(rim_color * f, 1.0); // add to lighting; f peaks at the silhouette
}
// Correctness: normal and view_dir MUST be in the same space and normalized.
よくある落とし穴
- 座標空間の混在(ワールド空間の法線をビュー空間のライトで照らすなど)は、わずかに誤ったシェーディングにつながります。空間を1つ選び、すべてをその空間に変換してください。
- 補間した法線や方向ベクトルの正規化忘れ:補間によってベクトルの長さが短くなるため、
dot()の結果がずれていきます。フラグメントステージでnormalize()を使ってください。 - エンジン間での UV の前提の違い。 エンジンによっては V を反転する(原点が左上か左下かの違い)ため、テクスチャが上下逆に表示されることがあります。使用するエンジンの規約を確認してください。
- 重い分岐や動的ループは GPU の処理を滞らせます。
step/smoothstep/mixを優先し、if/discardは本当にコストの低い早期終了に限って使ってください。 discardは早期 Z 判定を無効にするため、タイルベースのモバイル GPU ではパフォーマンスが低下することがあります。可能であればアルファブレンディングを使ってください。- モバイルでの精度:
highpとmediumpの違いは重要です。精度が低い場合、大きな UV 値や時間の値がちらつくことがあります。座標と時間には十分な精度を使ってください。 - GLSL と HLSL は同じという思い込み。
mix↔lerp、fract↔frac、texture()↔.Sample()、vec2↔float2、列優先と行優先の行列には違いがあります。対応表を参照してください。
参考資料
references/effects.md— 2D スプライトと 3D の完全なアウトライン、ビネット、カラーグレーディングのシェーダー、GLSL↔HLSL の関数/型の対応表、エンジンごとのメモ(Godotcanvas_item/spatial、Unity ShaderLab/HLSL、Unreal マテリアルノード)。
関連スキル
godot-shaders— Godot シェーディング言語の構文、組み込み機能、画面読み取り。unreal-niagara— GPU パーティクル VFX(異なるシェーダー用途)。procedural-gen— ディゾルブやプロシージャルテクスチャリングを駆動するノイズ。