defold-shaders-editing について
Defold の GLSL シェーダーファイルを作成・編集するスキルです。頂点・フラグメントプログラム、再利用可能なコード片、現代的な構文、マテリアルとの連携を扱います。
主な機能
- 01.vp、.fp、.glsl ファイルの編集
- 02現代的な GLSL 構文の指針
- 03シェーダーとマテリアルのデータ連携
- 04クロスコンパイルの制約を説明
活用シーン
- 01Defold の頂点・フラグメントシェーダー作成
- 02再利用可能な GLSL コード片の追加
- 03シェーダー変数とマテリアル設定の整合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ indiesoftby/ defold-agent-config --skill defold-shaders-editingインストール後、Claude Code で/defold-shaders-editingを入力して読み込まれたか確認します。
詳細
Defold シェーダーの編集
Defold のシェーダーファイル(頂点プログラム(.vp)、フラグメントプログラム(.fp)、GLSL インクルードスニペット(.glsl))を作成および編集します。
使用する場面
このスキルでは、Defold のレンダリングパイプラインで使用する GLSL シェーダーファイルを扱います。.material ファイルは対象外です。そちらには defold-proto-file-editing スキルを使用してください(references/material.md を参照)。
シェーダーパイプラインと GLSL バージョン
Defold は 2 つのシェーダーパイプラインに対応しています。
- レガシーパイプライン — OpenGL ES 2.0 互換の GLSL(
#versionディレクティブなし)で記述されたシェーダーです。Defold 1.9.2 以降は非推奨です。 - モダンパイプライン — SPIR-V 互換の GLSL(
#version 140以上)で記述されたシェーダーです。現在の標準です。
シェーダーは必ず、ファイルの先頭に #version 140(OpenGL 3.1)を指定して記述してください。 このディレクティブにより、ビルドプロセスでモダンパイプラインが選択されます。#version が見つからない場合、Defold はレガシーパイプラインにフォールバックします。
#version 140
クロスコンパイルとプラットフォームのターゲット
Defold は、1 つの GLSL ソースから複数のグラフィックス API 向けにシェーダーをコンパイルします。
- OpenGL 3.x / 4.x(デスクトップ:Windows、macOS、Linux)
- OpenGL ES 2.0 / 3.0(モバイル:Android、iOS)
- WebGL 1.0 / 2.0(HTML5)
- SPIR-V(Android、デスクトップでの Vulkan)
- Metal(iOS、macOS — SPIR-V クロスコンパイル経由)
このようにクロスコンパイルするため、すべての GLSL 機能がどの環境でも利用できるわけではありません。一部の関数(例:dFdx、dFdy、fwidth)は ES 2.0 / WebGL 1.0 のターゲットでは拡張機能が必要ですが、ES 3.0 以降 / デスクトップ GL では組み込み機能です。条件付き機能には #extension とプリプロセッサーガードを使用してください。
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
// Use derivative functions
#else
// Provide fallback
#endif
留意すべき主な制限事項:
- 境界が変数で決まる動的ループは、ES 2.0 / WebGL 1.0 では動作しない場合があります
- ES 2.0 では整数演算に制限があります
sampler2DArrayには ES 3.0 以降 / WebGL 2.0 以降が必要です- ストレージバッファーとコンピュートシェーダーには Vulkan / Metal が必要です
ファイルの種類
頂点プログラム(.vp)
頂点ごとに 1 回実行されます。頂点の位置をモデル/ワールド空間からスクリーン空間に変換します。gl_Position を出力し、out 変数を介してフラグメントシェーダーにデータを渡します。
フラグメントプログラム(.fp)
フラグメント(ピクセル)ごとに 1 回実行されます。最終的な色を計算します。ユーザー定義の out vec4 変数に出力します(#version 140 では非推奨の gl_FragColor は使用しません)。
GLSL インクルードスニペット(.glsl)
.vp または .fp ファイルから #include で読み込む、再利用可能な GLSL コードです。単独では実行されません。二重インクルードを防ぐため、ヘッダーガードを使用してください。
モダン GLSL の構文ルール(#version 140)
Attributes → in
頂点シェーダーでは、attribute の代わりに in を使用します。
in highp vec4 position;
in mediump vec2 texcoord0;
フラグメントシェーダーには頂点属性はありません。
Varyings → out / in
頂点シェーダーでは、varying の代わりに out を使用します。フラグメントシェーダーでは in を使用します。
// vertex shader
out mediump vec2 var_texcoord0;
// fragment shader
in mediump vec2 var_texcoord0;
フラグメント出力 → out vec4
非推奨の gl_FragColor の代わりに、宣言した out 変数を使用します。
out vec4 out_fragColor;
void main()
{
out_fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
Uniform ブロック
不透明型ではない uniform(行列、ベクトル、浮動小数点数)は、uniform ブロックに配置する必要があります。頂点シェーダーには vs_uniforms、フラグメントシェーダーには fs_uniforms を使用してください(慣例によるものです)。
uniform vs_uniforms
{
highp mat4 view_proj;
};
不透明型の uniform(サンプラー、イメージ)は、単独のままにします。
uniform mediump sampler2D texture_sampler;
uniform ブロックのメンバーには、ブロック名を付けずに名前で直接アクセスします。
gl_Position = view_proj * vec4(position.xyz, 1.0);
テクスチャサンプリング → texture()
非推奨の texture2D() / texture2DArray() の代わりに texture() を使用します。
vec4 color = texture(texture_sampler, var_texcoord0.xy);
精度修飾子
#version 140 では明示的な精度(lowp、mediump、highp)は任意です。パイプラインが、精度指定を必要とするプラットフォーム向けに自動設定します。ただし、明確にするため、またはプロジェクト内の既存コードのスタイルに合わせるために使用してもかまいません。周囲のファイルの規約に従ってください。
エディター固有のコード(EDITOR define)
Defold Editor のビューポートでシェーダーがレンダリングされるとき、プリプロセッサー define EDITOR を使用できます。#ifdef EDITOR を使って、エディターとゲームで動作を変えるコードを記述します。
#ifdef EDITOR
// Simplified rendering for editor preview
out_fragColor = texture(texture_sampler, var_texcoord0.xy);
#else
// Full rendering with effects for the game
out_fragColor = apply_effects(texture(texture_sampler, var_texcoord0.xy));
#endif
一般的な用途:
- パフォーマンス向上のため、エディターでは負荷の高いエフェクト(RGSS、ポストプロセッシング)を無効にする
- プレビューが適切に表示されないマテリアルに、デバッグ用またはフォールバック用のビジュアルを表示する
- 実行時のみのデータに依存する機能(例:スキニング)をスキップする
スニペットのインクルード(#include)
シェーダーファイルでは、プロジェクト相対パスまたはファイル相対パスを指定して、#include で .glsl スニペットをインクルードできます。
// Absolute (project-relative) path
#include "/main/my-snippet.glsl"
// Relative to current file
#include "my-snippet.glsl"
#include "../shared/utils.glsl"
ルール:
- インクルードできるのは
.glslファイルのみです - パスはプロジェクト内(またはライブラリー依存関係内)である必要があります
- 絶対パスは
/で始まります #includeを文の途中で使用することはできません
ヘッダーガード
二重インクルードを防ぐため、.glsl スニペットではヘッダーガードを使用します。
#ifndef MY_SNIPPET_GLSL
#define MY_SNIPPET_GLSL
// ... snippet code ...
#endif // MY_SNIPPET_GLSL
マテリアルとの関係
シェーダーとマテリアル(.material)は密接に連携しています。マテリアルファイルは、シェーダーが受け取るデータを定義します。マテリアルファイルを編集する場合は、defold-proto-file-editing スキルを使用してください(参照:references/material.md)。
定数(ユニフォーム)
マテリアルの vertex_constants / fragment_constants で宣言された定数は、シェーダー内のユニフォーム変数になります。ユニフォームブロック内に配置してください。
| マテリアルの定数型 | シェーダーのユニフォーム型 | 説明 |
|---|---|---|
CONSTANT_TYPE_VIEWPROJ | mat4 | ビュー行列 × プロジェクション行列を結合した行列 |
CONSTANT_TYPE_WORLD | mat4 | ワールド変換行列 |
CONSTANT_TYPE_VIEW | mat4 | ビュー(カメラ)行列 |
CONSTANT_TYPE_PROJECTION | mat4 | プロジェクション行列 |
CONSTANT_TYPE_WORLDVIEW | mat4 | ワールド行列 × ビュー行列 |
CONSTANT_TYPE_WORLDVIEWPROJ | mat4 | ワールド行列 × ビュー行列 × プロジェクション行列 |
CONSTANT_TYPE_NORMAL | mat4 | 法線行列 — transpose(inverse(view * world))。ワールド空間ではなく、ビュー空間の法線を生成します。 |
CONSTANT_TYPE_USER | vec4 | カスタムデータ。go.set() / go.animate() で変更可能 |
CONSTANT_TYPE_USER_MATRIX4 | mat4 | カスタム行列データ。go.set() で変更可能 |
マテリアルの name は、ユニフォームブロック内の変数名と一致している必要があります。
サンプラー
マテリアルの samplers セクションで宣言されたサンプラーは、sampler2D ユニフォームになります。マテリアルのサンプラー name は、シェーダーのユニフォーム名と一致している必要があります。
uniform mediump sampler2D texture_sampler; // Matches samplers { name: "texture_sampler" ... }
スプライト、タイルマップ、GUI、パーティクルでは、最初の sampler2D がコンポーネントの画像に自動的にバインドされます。
頂点属性
マテリアルの attributes セクションで宣言された属性(またはエンジンが提供するデフォルト属性)は、頂点シェーダー内の in 変数になります。
コンポーネントの種類ごとのデフォルト属性:
| コンポーネント | 属性 |
|---|---|
| スプライト | position、texcoord0 |
| タイルマップ | position、texcoord0 |
| GUI ノード | position、texcoord0、color |
| ParticleFX | position、texcoord0、color |
| モデル | position、texcoord0、normal |
| フォント | position、texcoord0、face_color、outline_color、shadow_color |
頂点空間
マテリアルの vertex_space 設定は、位置データがどのように渡されるかに影響します。
VERTEX_SPACE_WORLD(デフォルト)— 位置はワールド空間に事前変換されています。2D コンポーネント(スプライト、タイルマップ)で使用します。view_proj(またはCONSTANT_TYPE_VIEWPROJ)を使って、スクリーン空間へ直接変換します。VERTEX_SPACE_LOCAL— 位置はローカル/オブジェクト空間にあります。3D モデルで使用します。シェーダー内でworld → view → projectionを通して変換する必要があります。
インスタンシング
インスタンス化レンダリング(Model コンポーネント)では、mtx_world と mtx_normal をユニフォームではなく in 属性として宣言します。
in mediump mat4 mtx_world;
in mediump mat4 mtx_normal;
マテリアルには vertex_space: VERTEX_SPACE_LOCAL を設定する必要があります。これらの属性は、インスタンスごとのステップ関数用に自動的に設定されます。
法線行列(mtx_normal)— ビュー空間とワールド空間
組み込みの CONSTANT_TYPE_NORMAL は、CPU 上で transpose(inverse(view * world)) を計算します。これにより、法線はワールド空間ではなくビュー空間(カメラ空間)で得られます。
ビュー空間の法線で問題ない場合:
- すべてのライティング計算をビュー空間で行う場合
- カメラ位置が暗黙的に原点
(0,0,0)にある場合(スペキュラー計算が簡単になります)
ワールド空間の法線が必要な場合:
- キューブマップ反射、環境マッピング
- ワールド空間でのライティング、ワールド空間のエフェクト
ワールド空間の法線を得るには、随伴行列のトリックを使って頂点シェーダー内で mtx_world から法線行列を計算します(完全な inverse() より低コストです。余因子展開の代わりに外積を 3 回計算します)。
// transpose(adjugate(M)) for upper-left 3x3 of mat4.
// Equivalent to transpose(inverse(M)) up to a uniform scale factor,
// which is eliminated by normalize().
mat3 adjoint(mat4 m)
{
return mat3(
cross(m[1].xyz, m[2].xyz),
cross(m[2].xyz, m[0].xyz),
cross(m[0].xyz, m[1].xyz)
);
}
頂点シェーダーでの使用方法:
var_world_normal = normalize(adjoint(mtx_world) * normal.xyz);
| 目的 | 方法 |
|---|---|
| ビュー空間の法線 | 組み込みの mtx_normal(CONSTANT_TYPE_NORMAL)を使用 |
| ワールド空間の法線 | シェーダー内で normalize(adjoint(mtx_world) * normal.xyz) を計算 |
重要なルール: 座標空間を混在させないでください。ライトの方向がワールド空間にある場合、法線もワールド空間にする必要があります。
標準的な例
組み込み参照シェーダー
標準的なシェーダーの参考として .deps/builtins/materials/ を使用します。
sprite.vp/sprite.fp— 2D スプライト(ワールド空間、view_proj、tint)model.vp/model.fp— ライティングを含む 3D モデル(ローカル空間、mtx_worldview、mtx_normal、light)model_instanced.vp— インスタンシングを使用する 3D モデル(mtx_world、mtx_normalをin属性として使用)gui.vp/gui.fp— GUI ノード(ワールド空間、color属性、乗算済みアルファ)particlefx.vp/particlefx.fp— パーティクルエフェクトtile_map.vp/tile_map.fp— タイルマップskinning.glsl— スケルタルアニメーション用インクルード(フォールバックに#ifdef EDITORを使用)
GLSL インクルードのスニペット(ヘッダーガードと拡張機能を含む)
#ifndef MY_UTILS_GLSL
#define MY_UTILS_GLSL
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
// Utility function available on platforms that support derivatives
#if !defined(GL_ES) || __VERSION__ >= 300 || defined(GL_OES_standard_derivatives)
mediump float edge_smoothing(mediump float dist)
{
return smoothstep(0.0, fwidth(dist), dist);
}
#else
mediump float edge_smoothing(mediump float dist)
{
return step(0.0, dist);
}
#endif
#endif // MY_UTILS_GLSL
ワークフロー
新しいシェーダーペア(.vp + .fp)の作成
- コンポーネントの種類(スプライト、モデル、GUI など)を確認し、使用する属性と頂点空間を把握します。
- 対応する
.materialファイルを確認する(または作成を計画する)ことで、シェーダーが受け取る定数、サンプラー、属性を把握します。マテリアルの編集にはdefold-proto-file-editingスキルを使用します。 - 両方のファイルの先頭に
#version 140を記述します。 .vpでは、in属性、outバリアリング、定数を含むユニフォームブロックを宣言し、gl_Positionを計算します。.fpでは、inバリアリング、色出力用のout vec4、サンプラー、ユニフォームブロックを宣言し、出力色を計算します。- ユニフォーム名がマテリアルの定数名と完全に一致することを確認します。
- サンプラー名がマテリアルのサンプラー名と完全に一致することを確認します。
GLSL インクルードのスニペットの作成
.glslファイルを作成します。- ヘッダーガード(
#ifndef/#define/#endif)を追加します。 - すべてのターゲットで利用できるわけではない機能を使う場合は、プリプロセッサーガードとともに
#extensionディレクティブを追加します。 - 再利用可能な関数または定数を記述します。
既存のシェーダーの編集
- 現在のシェーダーファイルと対応する
.materialファイルを読みます。 - 要求された部分のみを変更します。
- 既存のコードスタイル(精度修飾子、命名、空白)を維持します。
- 新しいユニフォームはすべてマテリアルファイルにも追加します(
defold-proto-file-editingスキルを使用)。 - 削除したユニフォームはマテリアルファイルからも削除します。