コンテンツにスキップ

GLSL ライブコーディング

どのノードも、中身は全画面のフラグメントシェーダーです。 自分のシェーダーを書く GLSL ノードを作れるほか、組み込みノードのシェーダーも編集できます。保存するとその場で反映されます。

GLSL ノードを作る

  • ノードピッカーの GLSL タブ → GLSL (new file) で、新しいシェーダーファイルを持つノードを作ります。
  • 同じタブのファイル一覧(shaders/user/*.frag)から選ぶと、そのファイルのコピーを持つノードになります。

各ノードは自分のファイルを持ち(プロジェクトの shaders/)、編集してもそのノードだけが変わります。

編集する

  • アプリ内: ノードを選んで SHADER タブ。入力が止まったとき、または Ctrl+S / Cmd+S で保存します。
  • エージェントに頼む: SHADER タブの ASK AN AGENT に言葉で書くと、AI エージェントがシェーダーを書きます(AI エージェント連携)。
  • 外部エディタ: NODE タブか SHADER タブの OPEN IN EDITOR。エディタは LEMONADE_EDITOR、$VISUAL、$EDITOR の順に探します(例: LEMONADE_EDITOR="code --reuse-window")。

保存すると、#include したファイルの変更も含めてホットリロードされます。 コンパイルに失敗したときは直前のバージョンが動き続け、エラーが行番号付きでノードと SHADER タブに表示されます。

組み込みノード(Blur など)のシェーダーを編集すると、最初の編集でそのノード専用のコピーができます。ほかのノードは組み込みのままです。 REVERT TO BUILT-IN でコピーを捨てて元に戻せます。

シェーダーの書き方

#version 450
#include "util/common.glsl"

layout(location = 0) in vec2 v_uv;          // 0..1、左上が原点
layout(location = 0) out vec4 f_color;

// GLSL ノードのパラメータは、ここに書いたフィールド(順番どおり)
layout(set = 0, binding = 1) uniform NodeParams {
    float gain;    // 0..2 = 1        範囲と既定値。残りは説明
    int   shape;   // circle|square   選択肢(int)
    bool  invert;  // = true
    vec4  tint;    // color = 1,0.5,0
};

void main() {
    vec3 src = texture(u_tex0, v_uv).rgb;    // 最初の入力(未接続なら黒)
    float bass = u_bands.y;                  // 低域
    f_color = vec4(src + bass * gain * tint.rgb, 1.0);
}
  • NodeParams の float / int / bool / vec4(色)のフィールドが、NODE タブのパラメータになります。1 行に 1 つ。
  • // コメントの先頭に、範囲 min..max(float の既定 0..1、int は 0..10)、選択肢 a|b|c、color を書き、続けて = 既定値。
  • 保存のたびに読み直され、名前と型が同じパラメータは値が保たれます。
  • NodeParams がない(または読めない)ときは、0..1 のつまみ a..d が付きます。
  • パラメータもワイヤーや式で動かせます(パラメータを動かす)。

エンジンの値(util/common.glsl)

名前 意味
u_time 起動からの秒数
u_resolution 描いているサイズ(ピクセル)。アトラスマッピング中は送出フレームのサイズ
u_world_resolution ショーのフレームのサイズ(ピクセル)
world_uv(v_uv) このピクセルのショーのフレームでの位置(0..1)。アトラスマッピング以外では v_uv
sample_world(tex, uv) 入力 tex の、ショーのフレームの位置 uv の色
u_level 全体の音量(0..1)
u_bands 帯域(0..1): x sub(20–60 Hz)、y low(60–250 Hz)、z mid(250 Hz–2 kHz)、w high(2–16 kHz)
fft(i) スペクトラムのビン i(64 個、20 Hz–16 kHz、対数)
u_tex0 … u_tex3 入力テクスチャ(ポート順)。未接続は透明な黒
u_bpm テンポ
u_phase 拍の中の位置(0..1)
u_beat 拍ごとに 1 になり 0 へ減衰
u_beats 経過拍数(連続値)。floor(u_beats) で拍ごとに 1 進む
u_palette[8] パレットの 8 色
palette(t) パレットを t(0..1)で補間した色
u_triggers トリガーパッド 1〜4(x..w)。押すと 1、0 へ減衰
u_hits 帯域ごとのアタック(sub / low / mid / high)

よく使うパターン(トリガーのフラッシュ、ズームパンチ、ビートのステップ、音への反応)は シェーダーレシピ にあります。

アトラスマッピングに対応する

アトラスマッピングの間、ノードは送出フレームのレイアウトで描かれ、v_uv は送出フレームの位置です。会場で継ぎ目なくつながるように描くには:

  • 位置に使うのは world_uv(v_uv)。縦横比は world_aspect()、ピクセルの大きさは u_world_resolution で。
  • 同じピクセルの入力は、これまでどおり texture(u_tex0, v_uv)。
  • ほかのピクセルの入力(ぼかし、ずらし、回転など)は sample_world(u_tex0, world_uv(v_uv) + offset)。どのスロットにも映らない位置(スロットの間のすき間)は、近くのスロットの端の色を少しだけ延ばし(フレームの高さの 3 % まで)、その先は透明な黒になります。
  • util/uv.glsl の to_centered / sample_edge は会場の座標で動きます: to_centered(world_uv(v_uv))。
void main() {
    vec2 uv = world_uv(v_uv);
    vec2 d = vec2(8.0 * u_scale / u_world_resolution.x, 0.0);
    vec3 c = sample_world(u_tex0, uv + d).rgb;   // 8 px 右(となりのスロットからでも)
    f_color = vec4(c, 1.0);
}

アトラスマッピングを使わないときは、どれも v_uv、u_resolution、texture() と同じです。

Code ノード

Code ノードは、ソースコードをシンタックスハイライト付きの映像にします。既定では SHADER パネルで編集中のシェーダーを表示し、入力に合わせて更新されるので、ライブコーディングをそのまま見せられます。 Blend(Add / Screen)で映像に重ねます。