コンテンツにスキップ

シェーダーレシピ

GLSL ノードでよく使う短いパターン集です(原文は英語。プロジェクトフォルダの docs/shader-recipes.md と同じ内容)。 エンジンの値の意味は GLSL ライブコーディング を参照してください。

Short GLSL patterns for lemona.de shaders (GLSL nodes, shaders/user/*.frag). All of them assume #include "util/common.glsl" and the usual

layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 f_color;

See the uniform table in the README for what each u_* value means.

Band hits (u_hits)

u_hits.x … .w are the audio bands sub / low / mid / high. Each jumps to 1 when an attack in that band (a kick in low, a snare in mid, hats in high) rises above the band's threshold, and fades over the hit decay — both set in the AUDIO tab's "hits" section. They work like u_triggers, so every trigger snippet below works with a hit instead:

Effect Snippet Notes
Kick flash col += u_hits.y * 0.5; low band
Snare invert col = mix(col, 1.0 - col, u_hits.z); mid band
Hat sparkle col += step(0.97, hash21(floor(v_uv * 200.0) + u_time)) * u_hits.w; high band; needs #include "util/noise.glsl"
Kick zoom punch uv = (uv - 0.5) / (1.0 + u_hits.y * 0.2) + 0.5;

In expressions the same values are hit(0) … hit(3); the Audio Hit SIG node gives one band's value, a one-frame fired pulse and its attack.

Triggers (u_triggers)

u_triggers.x … .w are pads 1–4 (keys 1–4, or the pads in PERFORMANCE). Each jumps to 1 when fired and decays to 0 over the pad's decay time. The step sequencer in PERFORMANCE fires them on 16th-note steps of the beat clock (e.g. steps 1/5/9/13 = every beat, step 1 = every bar).

Effect Snippet Notes
Flash col += u_triggers.x; White flash that fades with the pad's decay
Blackout col *= 1.0 - u_triggers.x; Long decay = fade back in
Invert col = mix(col, 1.0 - col, u_triggers.x);
Zoom punch uv = (uv - 0.5) / (1.0 + u_triggers.x * 0.3) + 0.5; Zooms in around the center; apply before sampling / drawing
Shake uv += (hash2(vec2(u_time)) - 0.5) * 0.05 * u_triggers.x; Needs hash2 below
Time lurch float t = u_time + u_triggers.x * 2.0; Animation jumps ahead, then settles
Palette jump col = mix(col, palette(fract(t + 0.5)), u_triggers.x); Shifts to the opposite end of the palette
RGB split see below Needs an input texture (u_tex0)
Hard strobe col *= step(0.5, u_triggers.x); On/off instead of a fade
Snappier fade float k = pow(u_triggers.x, 3.0); Use k instead of the raw value: same decay time, sharper hit

RGB split on an input:

float d = 0.02 * u_triggers.x;
vec3 col = vec3(
    texture(u_tex0, uv + vec2(d, 0.0)).r,
    texture(u_tex0, uv).g,
    texture(u_tex0, uv - vec2(d, 0.0)).b);

A small hash for the shake:

vec2 hash2(vec2 p) {
    return fract(sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453);
}

Beat (u_beat, u_beats, u_phase, u_bpm)

Effect Snippet Notes
Pulse on every beat col *= 1.0 + u_beat * 0.5; u_beat is 1 on the beat, decaying
Step once per beat float step_ = floor(u_beats); e.g. pick a new pattern / color each beat
Lurch on the beat float kick = floor(u_beats) + smoothstep(0.0, 0.3, fract(u_beats)); Used by kleinian.frag to rotate in jumps
Every 4th beat float bar = floor(u_beats / 4.0);
Sweep within a beat float x = u_phase; 0 → 1 across each beat

Built-in nodes that react on their own: Glitch's drive (beat / trigger 1–4 / level) scales its strength, and Kaleidoscope's spin turns in time with the beat clock. Feedback reads its own previous frame as u_tex1; see shaders/effects/feedback.frag for a trail shader to copy.

Audio (u_level, u_bands, fft(i))

Effect Snippet Notes
Bass swell float s = 1.0 + u_bands.y * 0.4; y = low (60–250 Hz); x sub, z mid, w high
Hi-hat sparkle col += u_bands.w * noise;
Spectrum bars float h = fft(int(uv.x * 64.0)); 64 log-spaced bins, 0..1
Overall energy col *= 0.5 + u_level;
Finer spectrum / history as pixels connect an Audio FFT node to your node's input, then float v = texture(u_tex0, vec2(x, 0.5)).r; 512 log-spaced bins across (20 Hz left, 16 kHz right; axis linear: 0 → 16 kHz), brightness 0..1; mode spectrogram puts the last history seconds top (now) to bottom — e.g. texture(u_tex0, vec2(x, age / history)).r, or displace / mask with it. data flux: each bin's rise (onsets flash); chroma: 12 columns C..B
Waveform / vectorscope as pixels connect an Audio Scope node to your node's input Its output is the drawn trace (white on black): use it as a mask, displace by it, or feed Feedback for trails

Palette (palette(t), u_palette[8])

Effect Snippet
Gradient across the screen col = palette(uv.x);
Cycling colors col = palette(fract(uv.x + u_time * 0.1));
One palette color per beat col = u_palette[int(mod(floor(u_beats), 8.0))].rgb;
Grayscale (luma) col = monochrome(col);

Sizes in pixels (u_resolution, u_scale)

Nodes may render below 1920×1080 (render scale in the PERF window, saved with the show). u_world_resolution is the show's frame as actually rendered and u_scale its fraction of full size, so give sizes in full-resolution pixels and multiply by u_scale. With atlas mapping (MAPPING tab), nodes draw the sending layout instead: positions come from world_uv(v_uv) and other pixels of an input from sample_world(), so they join up across slots; without it these are v_uv, texture() and u_resolution:

Effect Snippet
Offset by 4 px vec2 d = 4.0 * u_scale / u_world_resolution;
One texel (neighbour sampling) vec2 texel = 1.0 / u_world_resolution;
A neighbour of this pixel sample_world(u_tex0, world_uv(v_uv) + texel * vec2(1, 0))
8 px pixel grid vec2 cell = 8.0 * u_scale / u_world_resolution;
Pixel row at full resolution float y = world_uv(v_uv).y * u_world_resolution.y / u_scale;

Param binding (no shader edits)

Any numeric node param can be driven from the NODE tab instead of in GLSL: FX binds an expression, IN gives the param an input port for a SIG wire (+ adds more, up to 4; the connected ones combine into x by mul / add / min / max / avg). In expressions, x is the combined wires (or the base value if nothing is connected), x1..x4 each wire (0 if unconnected), v is the base value set with the slider, and the engine values are time dt bpm phase beat beats level onset sub low mid high pi, trig(i), band(i), fft(i). Operators are C-like, including ? :. Results are clamped to the param's range. Full syntax, variables and functions: expressions.md.

Effect Expression Notes
Flash on pad 1 trig(0) > 0.5 ? 1 : 0.2
Pulse on the beat v + beat * 0.5
Bass-driven amount mix(v, 1, low)
Half-time sweep fract(beats / 2) * 360 - 180 e.g. Transform rotate
Scale a SIG wire x * 32 LFO (0..1) → Blur radius
LFO gated by a pad x1 * x2 (or IN ×2, mul) LFO → wire 1, Trigger → wire 2
Mix two wires mix(x1, x2, x3) wire 3 = crossfade
Every other bar mod(floor(beats / 4), 2) == 0 ? v : 0

SIG nodes (lemonade-core/src/signal_nodes.rs) do the same on wires: Trigger / Beat / Audio / Time / Constant, LFO / Noise, Math / Range / Expression / Smooth / Envelope, Compare / Switch / Counter / Hold.