makepad/apps/vj/resources/effects/218_extrude.splash
Admin cb49b032df vjfx: 104 presets, engine hooks + hold stage, the videomesh engine, the audio picture, livecodable effects, and mp4 thumbnail sheets
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vjfx: 104 new presets — the transition lane fills out and the screen family goes wide
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- fab: a 3D creation shell and the viewer built on it
2026-08-26 08:49:48 +02:00

129 lines
5.3 KiB
Text

// EXTRUDE — the picture stands up off the wall: every tile is pushed along
// the plane normal by its OWN brightness, so the clip becomes a relief that
// the swaying camera walks around.
//
// Pattern taught here: the tiles engine has two vertex hooks now, and both
// of them may READ THE CLIP. `extrude:` does the base push (and shades each
// tile by its own height, or a relief flattens out the moment the camera
// faces it); `fx_tile_spin` then tilts each tile by the LOCAL LUMA GRADIENT
// — four extra taps of input0 at the neighbouring tile centres — which is
// what turns a field of steps into a surface. `fx_tile` adds the dial and a
// slow bar breath on top.
{
name: "Extrude"
engine: "tiles"
mode: "hook"
seed: 17
grid: 32
spread: 8.4
aspect: 0.62
gap: 0.02
// The base relief, in world units of push per unit of luma.
extrude: 2.1
input0: "test"
speed: 1.0
beat_pulse: 0.45
beat_rate: 1.0
bar_beats: 4
// PERFORMANCE DIALS — mid-knob = the stock look, exact.
// DEPTH adds (or removes) relief on top of the baked `extrude`,
// TILT scales the gradient tilt, GROUT is the engine's own p2 lever.
p0: 0.5 p1: 0.5
p2: "0.04 + 0.10 * pulse"
dials: [
{name: "DEPTH", bind: "p0", default: 0.5},
{name: "TILT", bind: "p1", default: 0.5},
{name: "GROUT", bind: "p2", default: 0.5}
]
color_bg: #x05070d
color_a: #x9fd4ff
color_b: #x2a4a7a
color_c: #xdfefff
fog: 0.0
glow: 1.0
stages: [
{kind: "bloom", threshold: 0.74, strength: 0.75, levels: 3}
]
// THE LOOK LIVES HERE. This block is not a reference to the
// engine's shader — it IS the pixel math this effect runs.
// Rewrite it and the effect looks different; everything else
// (geometry, motion, the content coupling, the beat) keeps
// working, because the block SUBCLASSES the engine shader.
// Inputs: t = the mode highlight drive, attr = (shade, edge, stagger, tumble).
// Vertex hooks: fx_tile(id, grid, uv_window, t) -> (dx, dy, dz, scale),
// fx_tile_spin(id, grid, uv_window, t) -> (axis.xyz, angle).
// Signals: self.time_beat (time, beat, phase, pulse),
// self.sig (bar, bpm, energy, dt), self.user (p0..p3),
// self.tile (extrude, -, -, -), self.shape.y = tiles per side,
// self.col_a/b/c/bg, self.fog (density, glow, content, -).
shader: draw.DrawVjFxTiles {
// Extra depth on the DEPTH dial (0 at mid-knob) plus a slow bar
// breath, so the relief inhales once a bar instead of sitting dead.
fx_tile: fn(id: float, grid: vec2, uv_window: vec4, t: float) -> vec4 {
let gw = max(self.shape.y, 1.0)
let cuv = vec2(
(uv_window.x + uv_window.z) * 0.5,
(uv_window.y + uv_window.w) * 0.5
)
let lum = clamp(
dot(
self.tex0.sample_nearest(cuv, 0.0).xyz,
vec3(0.299, 0.587, 0.114)
),
0.0,
1.0
)
let dial = (clamp(self.user.x, 0.0, 1.0) - 0.5) * 2.6
let breath = 1.0 + 0.10 * sin(self.sig.x * 6.2831853)
return vec4(0.0, 0.0, lum * dial * breath, 1.0)
}
// The surface normal, from four taps of the clip at the
// neighbouring tile centres. The image's v runs the other way from
// the world's y, which is why the y gradient is (down - up).
fx_tile_spin: fn(id: float, grid: vec2, uv_window: vec4, t: float) -> vec4 {
let gw = max(self.shape.y, 1.0)
let stp = 1.0 / gw
let cuv = vec2(
(uv_window.x + uv_window.z) * 0.5,
(uv_window.y + uv_window.w) * 0.5
)
let w = vec3(0.299, 0.587, 0.114)
let ll = dot(self.tex0.sample_nearest(
vec2(clamp(cuv.x - stp, 0.0, 1.0), cuv.y), 0.0).xyz, w)
let rr = dot(self.tex0.sample_nearest(
vec2(clamp(cuv.x + stp, 0.0, 1.0), cuv.y), 0.0).xyz, w)
let dd = dot(self.tex0.sample_nearest(
vec2(cuv.x, clamp(cuv.y + stp, 0.0, 1.0)), 0.0).xyz, w)
let uu = dot(self.tex0.sample_nearest(
vec2(cuv.x, clamp(cuv.y - stp, 0.0, 1.0)), 0.0).xyz, w)
let gain = (0.4 + 1.2 * clamp(self.user.y, 0.0, 1.0))
* max(self.tile.x, 0.05) * 1.6
let g = vec2(rr - ll, dd - uu) * gain
let gl = max(length(g), 0.0001)
let ang = min(gl * 1.1, 1.0)
return vec4(g.y / gl, 0.0 - g.x / gl, 0.0, ang)
}
fx_color: fn(t: float, attr: vec4, content: vec4, cmix: float) -> vec4 {
let border = attr.y
// attr.x already carries the engine's height shade, so the
// relief has its own light before anything else happens here.
let lit = content.xyz * (attr.x * (0.30 + 0.70 * border))
// A cold sky bounce on the tops keeps the steps legible where
// the clip itself goes flat.
let sky = self.col_a.xyz * clamp(attr.x - 0.9, 0.0, 0.6) * 0.55
let grout = self.col_c.xyz * (1.0 - border)
* (t + clamp(self.user.z, 0.0, 4.0))
return vec4((lit + sky + grout) * self.fog.y, 1.0)
}
}
}