[per_vertex] — one binding, evaluated once per mesh vertex
index gives a binding one axis to vary along. [per_vertex] gives it two,
and it belongs to exactly one system: warp_mesh,
which covers the frame with a grid and resamples the previous frame through it.
A binding in that table is evaluated once per grid vertex per frame, with
x, y, rad and ang bound to that vertex’s own position.
system = "warp_mesh"
[mesh]x = 32y = 24
[per_vertex]# The past expands harder at the rim than at the centre — a tunnel that opens# out. No single `fb_zoom` can say this.zoom = "1.9 + rad * 0.9"# ...and turns at a rate that depends on which way round the frame you are.rot = "0.35 + sin(ang) * 0.30"The nine outputs it accepts — zoom, rot, cx, cy, dx, dy, sx, sy,
warp — are the per-vertex generalization of the fb_* affine, and
Parameter roster is the
authoritative table for what each does.
Converted MilkDrop presets do not ship. The public .milk collections have no clear
licensing, so no converted preset enters this repository or a release; the milkconv converter
plus a directory you point RLX_PRESET_DIR at is the whole import path. The question is
re-raised when the conversion-fidelity backlog (design-backlog 0106–0108) is worked off.
If you already have a converted directory, re-run
milkconvover it. The feedback field now emulates the reference’s 8-bit floor (ADR-0118) — without it, every dim residual survives in the float field and integrates, which is what turned classic presets pastel, white-hot, or tonally inverted. The switch is a runtime uniform, so an MD1-era preset — one with nowarp_shaderof its own — picks the fix up with no re-convert at all. What needs re-running is a bundle that does carry a converted warp shader: its epilogue was emitted before the quantizer existed and never calls it, so the uniform reaches nothing. Nothing breaks in the meantime; that preset renders exactly as it does today.A second reason to re-run: the video echo.
fVideoEchoAlpha,fVideoEchoZoomandnVideoEchoOrientationare read by the present pass — a second sampled copy of the frame, zoomed and flipped, summed over the first. This one always needs the re-convert, at every era: the three values are seeded into the bundle at conversion time, so a bundle emitted before this has no register for the scene to read. Only 2.4 % of the corpus sets a non-zero echo alpha, and where it does the preset is usually unrecognisable without it.
Things worth knowing before you reach for it:
- Each of the nine is also an ordinary scalar param, and the table overrides
it. Bind
zoomin[params]and the whole mesh takes that value; bind it in[per_vertex]as well and the table wins, vertex by vertex. So a preset starts from one shared transform and opts into a varying one output at a time. - Outside a
[per_vertex]table,x/y/rad/angread0— the same ruleindextakes outside a per-element evaluation. Unlikeindex, naming one in[params]is a load warning, because aradthat silently reads zero is a much more surprising thing to debug than anindexthat does. radandangare aspect-corrected against the render target, never against the mesh (ADR-0037), so arad-driven figure is round on a 16:9 monitor and round on a 5:4 one.[smoothing]cannot ease a per-vertex binding, for the reason it cannot ease a per-element one: the smoother holds one scalar and a series has no single value. Listing one is a surfaced load warning.- Cost is the reason
[mesh]is capped. It is(x+1) × (y+1) × (per-vertex bindings)evaluations per frame on the render thread — thousands, not dozens — so the grid is a tier capacity rather than an open number, and a preset asking for more than the tier carries gets the tier’s ceiling without an error. The measured ladder behind those numbers is onTierConfig::mesh_grid.
Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.