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
129 lines
5.3 KiB
Text
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)
|
|
}
|
|
}
|
|
}
|