shadertown 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/generated/sources.d.ts +3 -1
- package/dist/generated/sources.d.ts.map +1 -1
- package/dist/generated/sources.js +3 -0
- package/dist/generated/sources.js.map +1 -1
- package/dist/registry.d.ts +180 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +36 -0
- package/dist/registry.js.map +1 -1
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ deploys, not by this file.
|
|
|
6
6
|
This project follows [semantic versioning](https://semver.org). Before 1.0 the
|
|
7
7
|
minor number carries breaking changes, so pin it if that matters to you.
|
|
8
8
|
|
|
9
|
+
## 0.19.1 — 2026-09-09
|
|
10
|
+
|
|
11
|
+
- Brighten Strata’s shelf surfaces and edge lighting while retaining dark
|
|
12
|
+
recesses. Preserve palette brightness when blending warm and cool colors.
|
|
13
|
+
|
|
14
|
+
## 0.19.0 — 2026-09-09
|
|
15
|
+
|
|
16
|
+
- Add `strata`, animated contour shelves with recessed depth, rounded edge
|
|
17
|
+
lighting, and three color palettes.
|
|
18
|
+
- Tune the shape seed, depth point, rotation, stretch, and layer count.
|
|
19
|
+
Curated randomization ranges preserve shading and readable layers.
|
|
20
|
+
|
|
9
21
|
## 0.18.0 — 2026-09-09
|
|
10
22
|
|
|
11
23
|
- `ShaderCanvas` draws a shadertown credit in a corner of the canvas, as a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type GeneratedShaderName = "alloy" | "archipelago" | "ascii" | "aurora" | "basalt" | "beacon" | "billow" | "bloom" | "blossom" | "blueprint" | "bokeh" | "caldera" | "caustics" | "chladni" | "cloth" | "cumulus" | "dither" | "dunes" | "duotone" | "edges" | "eyes" | "ferro" | "filament" | "film-grain" | "flow" | "fluid" | "glass" | "gossamer" | "gravity" | "halftone" | "hills" | "island" | "julia" | "kaleidoscope" | "lagoon" | "lens" | "liquid-light" | "liquid-metal" | "louvres" | "lumen" | "maelstrom" | "magnet" | "meadow" | "melt" | "mesh-gradient" | "mosaic" | "neon-grid" | "neon-sign" | "orb" | "parallax" | "particles" | "pixel-clouds" | "pixel-fire" | "pixel-isle" | "pixel-rain" | "pixel-sea" | "plasma" | "pulsar" | "radiance" | "reveal" | "ripple" | "shafts" | "shatter" | "shockwave" | "shore" | "silk" | "spectra" | "starfield" | "swell" | "swirl" | "tesla" | "torch" | "truchet" | "tunnel" | "voronoi";
|
|
1
|
+
export type GeneratedShaderName = "alloy" | "archipelago" | "ascii" | "aurora" | "basalt" | "beacon" | "billow" | "bloom" | "blossom" | "blueprint" | "bokeh" | "caldera" | "caustics" | "chladni" | "cloth" | "cumulus" | "dither" | "dunes" | "duotone" | "edges" | "eyes" | "ferro" | "filament" | "film-grain" | "flow" | "fluid" | "glass" | "gossamer" | "gravity" | "halftone" | "hills" | "island" | "julia" | "kaleidoscope" | "lagoon" | "lens" | "liquid-light" | "liquid-metal" | "louvres" | "lumen" | "maelstrom" | "magnet" | "meadow" | "melt" | "mesh-gradient" | "mosaic" | "neon-grid" | "neon-sign" | "orb" | "parallax" | "particles" | "pixel-clouds" | "pixel-fire" | "pixel-isle" | "pixel-rain" | "pixel-sea" | "plasma" | "pulsar" | "radiance" | "reveal" | "ripple" | "shafts" | "shatter" | "shockwave" | "shore" | "silk" | "spectra" | "starfield" | "strata" | "swell" | "swirl" | "tesla" | "torch" | "truchet" | "tunnel" | "voronoi";
|
|
2
2
|
export declare const shaderSources: Readonly<Record<GeneratedShaderName, string>>;
|
|
3
3
|
/** Whether a shader invents its picture or reads one from a bound texture. */
|
|
4
4
|
export declare const shaderKinds: {
|
|
@@ -70,6 +70,7 @@ export declare const shaderKinds: {
|
|
|
70
70
|
readonly silk: "generative";
|
|
71
71
|
readonly spectra: "graph";
|
|
72
72
|
readonly starfield: "generative";
|
|
73
|
+
readonly strata: "generative";
|
|
73
74
|
readonly swell: "generative";
|
|
74
75
|
readonly swirl: "source";
|
|
75
76
|
readonly tesla: "generative";
|
|
@@ -151,6 +152,7 @@ export declare const shaderReadsSource: {
|
|
|
151
152
|
readonly silk: false;
|
|
152
153
|
readonly spectra: false;
|
|
153
154
|
readonly starfield: false;
|
|
155
|
+
readonly strata: false;
|
|
154
156
|
readonly swell: false;
|
|
155
157
|
readonly swirl: true;
|
|
156
158
|
readonly tesla: false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/generated/sources.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,aAAa,GACb,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,SAAS,GACT,UAAU,GACV,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,OAAO,GACP,SAAS,GACT,OAAO,GACP,MAAM,GACN,OAAO,GACP,UAAU,GACV,YAAY,GACZ,MAAM,GACN,OAAO,GACP,OAAO,GACP,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,QAAQ,GACR,OAAO,GACP,cAAc,GACd,QAAQ,GACR,MAAM,GACN,cAAc,GACd,cAAc,GACd,SAAS,GACT,OAAO,GACP,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,eAAe,GACf,QAAQ,GACR,WAAW,GACX,WAAW,GACX,KAAK,GACL,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,SAAS,CAAC;AAId,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/generated/sources.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,aAAa,GACb,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,SAAS,GACT,UAAU,GACV,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,OAAO,GACP,SAAS,GACT,OAAO,GACP,MAAM,GACN,OAAO,GACP,UAAU,GACV,YAAY,GACZ,MAAM,GACN,OAAO,GACP,OAAO,GACP,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,QAAQ,GACR,OAAO,GACP,cAAc,GACd,QAAQ,GACR,MAAM,GACN,cAAc,GACd,cAAc,GACd,SAAS,GACT,OAAO,GACP,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,eAAe,GACf,QAAQ,GACR,WAAW,GACX,WAAW,GACX,KAAK,GACL,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,GACR,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,SAAS,CAAC;AAId,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CA6EvE,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Ed,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6EpB,CAAC;AAEX,4EAA4E;AAC5E,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;CAgBf,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC"}
|
|
@@ -72,6 +72,7 @@ export const shaderSources = {
|
|
|
72
72
|
"silk": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// silk\n// ---------------------------------------------------------------------------\n\n// Silk — anisotropic sheen across warped fibre lines. Soft, cloth-like.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n var p = st_centered(uv) * params.scale;\n\n // Two-stage domain warp gives the fabric its folds.\n let w1 = vec2f(st_fbm2(p * 1.1 + vec2f(0.0, t * 0.08), 4),\n st_fbm2(p * 1.1 + vec2f(3.7, t * 0.06), 4));\n p = p + (w1 - 0.5) * params.warp * 2.0;\n let w2 = st_fbm2(p * 2.6 + vec2f(t * 0.05, 0.0), 5);\n\n // Fibre direction: dense parallel lines bent by the warp.\n let fibres = sin((p.x * 18.0 + p.y * 6.0) * params.a + w2 * 12.0 * params.b);\n let sheen = pow(clamp(fibres * 0.5 + 0.5, 0.0, 1.0), 3.0);\n\n // The base tone comes from the fold height, the highlight from the fibres.\n var col = st_palette(clamp(w2 * 1.4 - 0.1, 0.0, 1.0));\n col = col + st_palette(1.0) * sheen * params.c;\n\n // Slight darkening in the creases sells the depth.\n let crease = smoothstep(0.55, 0.15, w2);\n col = col * (1.0 - crease * params.d * 0.7);\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
73
73
|
"spectra": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// shadertown graph prelude — prepended (after the shared prelude) to every\n// graph shader at build time, whatever its flavour.\n//\n// A graph shader is not one pass but a whole render graph in a single module,\n// each pass its own entry point, and the runtime (src/graph.ts) drives them\n// with `draw()` and explicit entry names. This file carries the part every\n// flavour shares: the full-screen vertex stage, the present pass, and an\n// Unreal-style bloom chain that reads whatever HDR scene the flavour drew.\n// `effect()` picks the first vertex and fragment entries, which is why\n// `vs_fullscreen` and `fs_main` (the present pass) are declared first.\n//\n// The flavour prelude that follows this one (src/shaders/graph/<flavour>/)\n// declares the passes that draw the scene, and the variant file below that\n// supplies the functions the flavour asks for:\n//\n// particles a displacement field, normals, an instanced particle draw\n// drafting a source layer measured, distance-fielded and drawn as a plate\n// glow one scene pass of light on dark, bloomed\n// studio sculpted meshes, ray-traced glass, depth of field and bloom\n// masonry a floor of slabs as instanced boxes, depth of field and bloom\n//\n// Bindings are declared once for the whole module; vgpu builds layouts per\n// entry point, so each pass only owes the bindings it actually reads. The\n// shared passes bind from 20 upward, leaving 1 to 19 to the flavour — the\n// drafting flavour spends 1 to 3 on the source prelude.\n//\n// The bloom chain and present pass are ported from the vgpu `fft-ocean`\n// (\"Particles ocean\") example and keep its behaviour: five blurred octaves\n// recombined, linear to sRGB on the way out.\n\nstruct BlurUniforms {\n direction : vec2f,\n invSize : vec2f,\n coeffs0 : vec4f,\n coeffs1 : vec4f,\n coeffs2 : vec4f,\n coeffs3 : vec4f,\n coeffs4 : vec4f,\n coeffs5 : vec4f,\n}\n\nstruct BloomUniforms {\n threshold : f32,\n smoothWidth : f32,\n strength : f32,\n radius : f32,\n factors0 : vec4f,\n factors1 : vec4f,\n}\n\n@group(0) @binding(20) var<uniform> blurU : BlurUniforms;\n@group(0) @binding(21) var<uniform> bloomU : BloomUniforms;\n@group(0) @binding(22) var t_scene : texture_2d<f32>;\n@group(0) @binding(23) var t_input : texture_2d<f32>;\n@group(0) @binding(24) var t_blur1 : texture_2d<f32>;\n@group(0) @binding(25) var t_blur2 : texture_2d<f32>;\n@group(0) @binding(26) var t_blur3 : texture_2d<f32>;\n@group(0) @binding(27) var t_blur4 : texture_2d<f32>;\n@group(0) @binding(28) var t_blur5 : texture_2d<f32>;\n@group(0) @binding(29) var t_bloom : texture_2d<f32>;\n@group(0) @binding(30) var linearSampler : sampler;\n\nstruct FullscreenOut {\n @builtin(position) position : vec4f,\n @location(0) uv : vec2f,\n}\n\n// Same triangle and uv orientation vgpu generates for a plain effect, so the\n// fullscreen fragment entries below read uv exactly like every other shader.\n@vertex fn vs_fullscreen(@builtin(vertex_index) vi : u32) -> FullscreenOut {\n var pos = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n var uv = array<vec2f, 3>(vec2f(0.0, 1.0), vec2f(2.0, 1.0), vec2f(0.0, -1.0));\n var out : FullscreenOut;\n out.position = vec4f(pos[vi], 0.0, 1.0);\n out.uv = uv[vi];\n return out;\n}\n\n/** sRGB well to linear light, so the present pass's sRGB step undoes it. */\nfn st_toLinear(c : vec3f) -> vec3f {\n return pow(max(c, vec3f(0.0)), vec3f(2.2));\n}\n\n// --- present ---------------------------------------------------------------\n// First fragment entry on purpose: the runtime's `effect()` for this shader\n// resolves to it, and the Stage drives it like any other shader's fragment.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let scene = textureSample(t_scene, linearSampler, uv);\n let bloom = textureSample(t_bloom, linearSampler, uv);\n // The generator supplies an identity hook unless the flavour opts into\n // tone mapping. Existing emissive graphs retain their original response.\n let linear = max(st_graphTone(scene.rgb + bloom.rgb), vec3f(0.0));\n let lt = linear * 12.92;\n let gt = 1.055 * pow(linear, vec3f(0.41666)) - vec3f(0.055);\n let srgb = select(gt, lt, linear <= vec3f(0.0031308));\n return st_post(srgb, uv);\n}\n\n// --- bloom -------------------------------------------------------------------\n\nfn st_luminance(rgb : vec3f) -> f32 {\n return dot(rgb, vec3f(0.299, 0.587, 0.114));\n}\n\n@fragment fn fs_bright(@location(0) uv : vec2f) -> @location(0) vec4f {\n // LuminosityHighPassShader, as ported by the fft-ocean example.\n let texel = textureSample(t_scene, linearSampler, uv);\n let gate = smoothstep(bloomU.threshold, bloomU.threshold + bloomU.smoothWidth, st_luminance(texel.xyz));\n return mix(vec4f(vec3f(0.0), 0.0), texel, gate);\n}\n\n/**\n * The bloom gate for a scene that says itself what is light: its alpha. A\n * pale mat or a bright ground would otherwise bloom as a whole.\n */\n@fragment fn fs_brightAlpha(@location(0) uv : vec2f) -> @location(0) vec4f {\n let texel = textureSample(t_scene, linearSampler, uv);\n return vec4f(texel.rgb * texel.a, texel.a);\n}\n\nconst ST_BLUR_RADIUS : u32 = 22u;\n\nfn st_blurCoefficient(i : u32) -> f32 {\n let packed = array<vec4f, 6>(\n blurU.coeffs0, blurU.coeffs1, blurU.coeffs2,\n blurU.coeffs3, blurU.coeffs4, blurU.coeffs5,\n );\n return packed[i / 4u][i % 4u];\n}\n\n@fragment fn fs_blur(@location(0) uv : vec2f) -> @location(0) vec4f {\n // UnrealBloomPass separable blur: coefficients past each level's radius are\n // zero, so one fixed loop serves all five levels.\n var weightSum = st_blurCoefficient(0u);\n var diffuseSum = textureSample(t_input, linearSampler, uv).rgb * weightSum;\n for (var i = 1u; i < ST_BLUR_RADIUS; i = i + 1u) {\n let w = st_blurCoefficient(i);\n let offset = blurU.direction * blurU.invSize * f32(i);\n let s1 = textureSample(t_input, linearSampler, uv + offset).rgb;\n let s2 = textureSample(t_input, linearSampler, uv - offset).rgb;\n diffuseSum = diffuseSum + (s1 + s2) * w;\n }\n return vec4f(diffuseSum, 1.0);\n}\n\nfn st_bloomFactor(i : u32) -> f32 {\n let v = array<f32, 8>(\n bloomU.factors0.x, bloomU.factors0.y, bloomU.factors0.z, bloomU.factors0.w,\n bloomU.factors1.x, bloomU.factors1.y, bloomU.factors1.z, bloomU.factors1.w,\n );\n let mirror = 1.2 - v[i];\n return mix(v[i], mirror, bloomU.radius);\n}\n\n@fragment fn fs_composite(@location(0) uv : vec2f) -> @location(0) vec4f {\n // UnrealBloomPass composite, tint colours all white.\n let bloom = 3.0 * bloomU.strength * (\n st_bloomFactor(0u) * textureSample(t_blur1, linearSampler, uv).rgb +\n st_bloomFactor(1u) * textureSample(t_blur2, linearSampler, uv).rgb +\n st_bloomFactor(2u) * textureSample(t_blur3, linearSampler, uv).rgb +\n st_bloomFactor(3u) * textureSample(t_blur4, linearSampler, uv).rgb +\n st_bloomFactor(4u) * textureSample(t_blur5, linearSampler, uv).rgb\n );\n return vec4f(bloom, max(bloom.r, max(bloom.g, bloom.b)));\n}\n\nfn st_graphTone(c : vec3f) -> vec3f { return c; }\n\n// shadertown glow prelude — prepended (after the shared and graph preludes)\n// to every shader in src/shaders/graph/glow/.\n//\n// The simplest flavour: one full-screen scene pass into the HDR target, then\n// the shared bloom chain and the present pass. It exists for the shaders that\n// are light on dark — threads, beams, scanned bands — where a real bloom is\n// the difference between a drawing of light and light. The variant file below\n// this prelude supplies exactly one function:\n//\n// fn st_scene(uv : vec2f) -> vec4f\n//\n// `uv` is the canvas uv; the return value is linear-light colour in rgb, and\n// in w how much of the pixel is light the chain may bleed. The present pass\n// converts to sRGB on the way out, so wells go through `st_toLinear` first.\n\n@fragment fn fs_scene(@location(0) uv : vec2f) -> @location(0) vec4f {\n return st_scene(uv);\n}\n\n// ---------------------------------------------------------------------------\n// spectra\n// ---------------------------------------------------------------------------\n\n// Spectra — colour-split bands over a rippling field, swept by a slow pulse.\n//\n// A field of crossed sines bends a stack of horizontal bands, a pulse travels\n// down them and lights the ones it passes, and each colour channel reads the\n// band pattern a little offset from the others, so the edges fringe into\n// spectra the way a cheap prism does. Scanlines over the top; the pulse\n// tightens and brightens under the pointer.\n// colorA the dark end of the ramp, colorB the mid, colorC the peak\n// a band count, b sweep width, c sharpness, d split; warp is the ripple\n\nfn spectraField(p : vec2f, t : f32) -> f32 {\n var w = sin(p.x * 1.3 + t * 0.7);\n w = w + sin(p.y * 1.7 - t * 0.52) * 0.8;\n w = w + sin((p.x + p.y) * 0.9 + t * 0.91) * 0.6;\n w = w + sin((p.x - p.y) * 1.53 - t * 0.63) * 0.42;\n return w * 0.35;\n}\n\n/** The three-well ramp, with a little contrast pushed into it. */\nfn spectraRamp(f : f32, low : vec3f, mid : vec3f, peak : vec3f) -> vec3f {\n let x = pow(clamp(f, 0.0, 1.0), 1.15);\n let c = mix(low, mid, smoothstep(0.08, 0.6, x));\n return mix(c, peak, smoothstep(0.68, 1.0, x));\n}\n\n/** One band: a raised cosine sharpened by a power, softened by `aa`. */\nfn spectraBand(x : f32, aa : f32, sharp : f32) -> f32 {\n let v = mix(0.5, 0.5 + 0.5 * cos(x * TAU), aa);\n return pow(v, sharp);\n}\n\nfn st_scene(uv : vec2f) -> vec4f {\n let res = params.resolution;\n let aspect = st_aspect();\n // Centred, aspect-corrected, y up: the pulse travels down the frame.\n let uv0 = vec2f((uv.x * 2.0 - 1.0) * aspect, 1.0 - uv.y * 2.0);\n let p = uv0 / max(clamp(params.scale, 0.4, 2.5) * 1.5, 0.001);\n let t = st_time() * 0.5;\n\n // The hand: a soft boost of contrast and light around it.\n let pointer = params.pointer;\n let moved = step(1e-5, length(pointer - 0.5));\n let hand = vec2f((pointer.x * 2.0 - 1.0) * aspect, 1.0 - pointer.y * 2.0);\n let toHand = uv0 - hand;\n let boost = exp(-dot(toHand, toHand) / 0.25) * 0.5 * moved;\n\n let ripple = clamp(params.warp, 0.0, 1.0);\n let sig = spectraField(p * 2.0, t);\n let coord = p.y + sig * ripple;\n\n // The pulse lights what it passes; the rest of the stack keeps a low glow,\n // so a frame the pulse has left is a dim pattern rather than a black one.\n let sweepWidth = max(params.b, 0.05);\n let phase = coord / sweepWidth - t * 0.25;\n let pulse = pow(0.5 + 0.5 * cos(phase * TAU), 6.0);\n let sweep = mix(0.32, 1.0, pulse);\n\n let lc = coord * clamp(params.a, 3.0, 24.0);\n var aa = 1.0 / (1.0 + 1.4 * fwidth(lc) * 3.0);\n aa = clamp(aa * (1.0 + boost * 0.6), 0.0, 1.0);\n\n let bodyBase = clamp(0.5 + 0.5 * sig, 0.0, 1.0);\n let body = bodyBase * bodyBase * 0.22 * sweep;\n\n let sharp = max(params.c, 0.1);\n let split = clamp(params.d, 0.0, 1.5) * 0.16;\n let fr = clamp(spectraBand(lc + split, aa, sharp) * sweep + body, 0.0, 1.0);\n let fg = clamp(spectraBand(lc, aa, sharp) * sweep + body, 0.0, 1.0);\n let fb = clamp(spectraBand(lc - split, aa, sharp) * sweep + body, 0.0, 1.0);\n\n let low = st_toLinear(params.colorA.rgb);\n let mid = st_toLinear(params.colorB.rgb);\n let peak = st_toLinear(params.colorC.rgb);\n let col = vec3f(\n spectraRamp(fr, low, mid, peak).r,\n spectraRamp(fg, low, mid, peak).g,\n spectraRamp(fb, low, mid, peak).b,\n );\n\n var lit = (fr + fg + fb) * 0.3333333 * params.intensity * (1.0 + boost * 0.9);\n // Scanlines, one every few device pixels.\n lit = lit * (1.0 - 0.18 * (0.5 + 0.5 * cos(uv.y * res.y * 1.7)));\n lit = clamp(lit, 0.0, 1.0);\n\n return vec4f(col * lit, lit);\n}\n",
|
|
74
74
|
"starfield": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// starfield\n// ---------------------------------------------------------------------------\n\n// Starfield — a warp corridor. Layered depth slices flying at the camera.\n\n// Two independent hashes per cell; sharing one seed across layers is what\n// produces the diagonal moiré you get from adding a scalar to both components.\nfn cellRandom(cell : vec2f, layerSeed : f32) -> vec3f {\n let a = st_hash22(cell + vec2f(layerSeed * 37.19, layerSeed * 11.73));\n let b = st_hash21(cell.yx + layerSeed * 91.31);\n return vec3f(a, b);\n}\n\nfn layer(p : vec2f, layerIndex : f32, t : f32) -> vec3f {\n let z = fract(layerIndex * 0.1667 - t * 0.12);\n let scaleByDepth = mix(16.0, 2.0, z);\n let fade = smoothstep(0.0, 0.22, z) * smoothstep(1.0, 0.7, z);\n if (fade <= 0.0) { return vec3f(0.0); }\n\n // Rotating each slice breaks the alignment between neighbouring layers.\n let grid = st_rot(layerIndex * 1.37) * p * scaleByDepth;\n let base = floor(grid);\n let frac = fract(grid) - 0.5;\n\n var acc = vec3f(0.0);\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let neighbour = vec2f(f32(x), f32(y));\n let rnd = cellRandom(base + neighbour, layerIndex);\n if (rnd.z < 0.72) { continue; }\n\n let point = neighbour + rnd.xy - 0.5;\n var offset = frac - point;\n // Motion blur: stretch each star along its radial direction.\n let radial = normalize(p + vec2f(1e-5));\n let along = dot(offset, radial);\n let across = offset - radial * along;\n offset = radial * along / (1.0 + params.b * 8.0 * (1.0 - z)) + across;\n\n // A tight inverse-square core keeps stars as points. Widen the epsilon\n // and six layers of them blur into fog.\n let d = length(offset);\n let brightness = 0.00022 * params.a * (0.35 + rnd.z);\n acc = acc + st_palette(rnd.y) * (brightness / (d * d + 0.000018)) * fade;\n }\n }\n return acc;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n var p = st_centered(uv) * params.scale;\n let t = st_time();\n p = st_rot(t * 0.05 * params.d) * p;\n\n var col = vec3f(0.004, 0.005, 0.012);\n for (var i = 0; i < 6; i = i + 1) {\n col = col + layer(p, f32(i), t);\n }\n\n // Core glow at the vanishing point.\n let core = params.c * 0.05 / (length(p) + 0.06);\n col = col + st_palette(1.0) * core;\n\n return st_post(st_tonemap(col * params.intensity), uv);\n}\n",
|
|
75
|
+
"strata": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// strata\n// ---------------------------------------------------------------------------\n\n// Nested contour shelves share one field, so they cannot cross as they move.\nfn strata_field(p: vec2f, t: f32) -> f32 {\n let phase = params.d * 2.399963;\n let bend = vec2f(\n sin(p.y * 2.9 + t * 0.23 + phase) * 0.24 + sin(p.y * 5.1 - t * 0.13 + phase * 0.7) * 0.08,\n sin(p.x * 2.6 - t * 0.19 + phase * 1.3) * 0.22 + cos(p.x * 4.3 + t * 0.17 + phase * 0.9) * 0.09,\n );\n let q = p + bend * params.warp - vec2f(0.18, -0.08);\n return length(q * vec2f(0.85 / params.h, 1.12 * params.h))\n + sin(q.x * 3.4 + q.y * 2.1 + t * 0.11 + phase) * 0.075 * params.warp;\n}\n\n@fragment fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let centered = st_centered(uv) - vec2f(params.e * st_aspect(), params.f);\n let p = st_rot(params.g * PI / 180.0) * centered * params.scale * 2.4;\n let field = strata_field(p, st_time());\n let gradient = vec2f(dpdx(field), dpdy(field));\n let normal = gradient / max(length(gradient), 0.00001);\n let light = clamp(dot(normal, normalize(vec2f(-0.6, -0.8))), -1.0, 1.0);\n let aa = max(fwidth(field), 0.0001);\n let spacing = 1.65 / params.a;\n let warm = 1.0 - smoothstep(-0.85, 0.65, p.x + p.y * 0.85);\n var col = params.colorA.rgb * 0.13;\n\n for (var i = 0; i < 12; i = i + 1) {\n if (f32(i) >= params.a) { break; }\n let level = f32(i);\n let edge = 0.24 + level * spacing;\n let d = field - edge;\n let mask = smoothstep(-aa, aa, d);\n let rise = smoothstep(0.0, spacing * 0.6, max(d, 0.0));\n let depth = 0.32 + 0.68 * (1.0 - exp(-level * 0.62));\n let cool = mix(params.colorA.rgb, params.colorB.rgb, 0.5 + 0.5 * sin(level * 0.95 - 1.1));\n let pink = vec3f(params.colorC.r, params.colorA.g * 0.7, params.colorA.b * 0.85);\n let innerWarm = mix(params.colorA.rgb, pink, smoothstep(0.0, 3.0, level));\n let hot = mix(innerWarm, params.colorC.rgb, smoothstep(3.0, 8.0, level));\n // Keep the palette's brightness through blends between opposing hues.\n let blend = mix(cool, hot, warm);\n let peak = max(blend.r, max(blend.g, blend.b));\n let brightness = mix(max(cool.r, max(cool.g, cool.b)), max(hot.r, max(hot.g, hot.b)), warm);\n let pigment = st_saturation(blend * brightness / max(peak, 0.001), 1.12) * depth;\n let shade = mix(1.0, 0.46 + 0.62 * rise + 0.12 * light, mix(0.65, 1.0, params.b));\n var shelf = pigment * shade;\n // A narrow lip and broad shoulder suggest a bevel without a ray march.\n let lip = exp(-max(d, 0.0) / max(aa, spacing * 0.025));\n let shoulder = exp(-max(d, 0.0) / (spacing * 0.22));\n shelf = shelf + (pigment + vec3f(0.15)) * (lip * 0.85 + shoulder * 0.32)\n * (0.7 + 0.3 * max(light, 0.0)) * params.c;\n col = mix(col, shelf, mask);\n }\n return st_post(col * params.intensity, uv);\n}\n",
|
|
75
76
|
"swell": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// swell\n// ---------------------------------------------------------------------------\n\n// Swell — open ocean marched as a heightfield, so waves hide waves.\n//\n// The surface is real: eight steps of interpolation search land the ray on a\n// displaced heightfield, which is what buys actual silhouettes — a crest in\n// front occluding the trough behind it — instead of a flat plane wearing\n// normal-map make-up. Each octave is a product of the two axes of a\n// noise-warped sine, sharpened by a choppiness power: sharp crests, round\n// troughs, and no criss-cross lattice. The march reads three octaves, the\n// normal five; nobody can tell.\n\nfn waveOctave(uv : vec2f, choppy : f32) -> f32 {\n let n = st_noise2(uv) * 2.0 - 1.0;\n let q = uv + vec2f(n, n);\n let wv = vec2f(1.0) - abs(sin(q));\n let sw = abs(cos(q));\n let m = mix(wv, sw, wv);\n return pow(1.0 - pow(m.x * m.y, 0.65), choppy);\n}\n\nfn seaHeight(p2 : vec2f, t : f32, octaves : i32) -> f32 {\n var freq = 0.16;\n var amp = 0.62 * params.b;\n var choppy = mix(1.2, 4.5, clamp(params.d, 0.0, 2.0) * 0.5);\n var uv = vec2f(p2.x * 0.75, p2.y);\n var h = 0.0;\n let count = st_steps(octaves);\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= count) { break; }\n var d = waveOctave((uv + vec2f(t, t)) * freq, choppy);\n d = d + waveOctave((uv - vec2f(t, t)) * freq, choppy);\n h = h + d * amp;\n uv = mat2x2f(vec2f(1.6, -1.2), vec2f(1.2, 1.6)) * uv;\n freq = freq * 1.9;\n amp = amp * 0.22;\n choppy = mix(choppy, 1.0, 0.2);\n }\n return h;\n}\n\nfn skyColor(dir : f32, sunAmt : f32) -> vec3f {\n var sky = mix(mix(params.colorA.rgb, vec3f(0.9, 0.93, 0.95), 0.75), params.colorA.rgb * 1.15, smoothstep(-0.02, 0.5, dir));\n sky = mix(sky, params.colorC.rgb, pow(sunAmt, 4.0) * 0.5);\n return sky;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time() * 0.8;\n let p = st_centered(uv);\n\n let look = (params.pointer - vec2f(0.5)) * 0.15;\n\n let maxAmp = 1.35 * params.b;\n let ro = vec3f(0.0, maxAmp + 2.2, t * 1.2);\n let rd = normalize(vec3f(p.x + look.x * 0.2, -p.y * 0.8 - 0.14 + look.y * 0.15, 1.15));\n\n let sunDir = normalize(vec3f(0.15 - look.x * 0.2, 0.03 + 0.3 * params.a, 0.9));\n let sunAmt = clamp(dot(rd, sunDir), 0.0, 1.0);\n\n var sky = skyColor(rd.y, sunAmt);\n sky = sky + params.colorC.rgb * pow(sunAmt, 1400.0) * 4.0;\n sky = sky + params.colorC.rgb * pow(sunAmt, 32.0) * 0.5;\n\n var col = sky;\n if (rd.y < -0.01) {\n // Bracket the crossing: the ray is above every wave at tm and below sea\n // floor level at tx, then eight secant steps pin the surface.\n var tm = (maxAmp - ro.y) / rd.y;\n var tx = -ro.y / rd.y * 1.6;\n var hm = ro.y + rd.y * tm - seaHeight((ro + rd * tm).xz, t, 3);\n var hx = ro.y + rd.y * tx - seaHeight((ro + rd * tx).xz, t, 3);\n var travelled = tx;\n let refine = st_steps(8);\n for (var i = 0; i < refine; i = i + 1) {\n travelled = mix(tm, tx, hm / max(hm - hx, 1e-4));\n let pos = ro + rd * travelled;\n let h = pos.y - seaHeight(pos.xz, t, 3);\n if (h < 0.0) {\n tx = travelled;\n hx = h;\n } else {\n tm = travelled;\n hm = h;\n }\n }\n let pos = ro + rd * travelled;\n\n // Normal from central differences of the five-octave field, the step\n // widening with distance so far water stays calm instead of fizzing.\n let e = max(0.008 * travelled, 0.01);\n let h0 = seaHeight(pos.xz, t, 5);\n let n = normalize(vec3f(\n seaHeight(pos.xz - vec2f(e, 0.0), t, 5) - seaHeight(pos.xz + vec2f(e, 0.0), t, 5),\n 2.0 * e,\n seaHeight(pos.xz - vec2f(0.0, e), t, 5) - seaHeight(pos.xz + vec2f(0.0, e), t, 5),\n ));\n\n // Fresnel decides sky against body.\n let fres = 0.03 + 0.5 * pow(1.0 - clamp(dot(n, -rd), 0.0, 1.0), 3.0);\n let refl = skyColor(reflect(rd, n).y, sunAmt);\n\n // The body: deep colour lifted toward the crest colour with height —\n // the wrapped light that reads as the sun inside the wave.\n let lift = clamp(h0 / maxAmp, 0.0, 1.0);\n var body = params.colorA.rgb * 0.38;\n body = body + params.colorB.rgb * pow(clamp(dot(n, sunDir), 0.0, 1.0) * 0.35 + 0.65, 3.0) * lift * 0.75;\n\n col = mix(body, refl, fres);\n\n // Whitecaps where a crest peaks, eaten by noise into streaks.\n let cap = smoothstep(0.72, 0.95, lift) * smoothstep(0.35, 0.75, st_noise2(pos.xz * 1.6 + t * 0.8));\n col = mix(col, vec3f(0.93, 0.96, 0.96), cap * 0.85);\n\n // Glitter: tight and sharp nearby, a broad streak at distance.\n let spec = pow(clamp(dot(reflect(rd, n), sunDir), 0.0, 1.0), 260.0 / (1.0 + travelled * travelled * 0.02));\n col = col + params.colorC.rgb * spec * params.c * 2.0;\n\n // Haze folds sea into sky at the horizon.\n col = mix(col, sky, 1.0 - exp(-max(travelled - 6.0, 0.0) * 0.022));\n }\n\n return st_post(st_tonemap(col * params.intensity), uv);\n}\n",
|
|
76
77
|
"swirl": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// shadertown source prelude — appended to the shared prelude for shaders that\n// read a layer instead of inventing one.\n//\n// Every binding here is additive. A generative shader declares none of them and\n// keeps the exact layout it always had, and `Source` is its own uniform block\n// rather than four more fields on `Params`, so the ABI that every already\n// shipped shader was built against never moves.\n\n@group(0) @binding(1) var srcTexture : texture_2d<f32>;\n@group(0) @binding(2) var srcSampler : sampler;\n\nstruct Source {\n // Pixel size of the bound texture. Zero means \"assume it matches the canvas\".\n size : vec2f,\n // 0 stretches the source over the canvas, 1 covers the canvas and crops.\n fit : f32,\n _pad : f32,\n}\n\n@group(0) @binding(3) var<uniform> source : Source;\n\n// How much of the source each axis shows once it is fitted. Both components are\n// 1 or less: covering means cropping, never adding.\nfn st_srcScale() -> vec2f {\n if (source.fit < 0.5 || source.size.x < 1.0 || source.size.y < 1.0) {\n return vec2f(1.0);\n }\n let canvas = st_aspect();\n let src = source.size.x / source.size.y;\n if (src > canvas) {\n return vec2f(canvas / src, 1.0);\n }\n return vec2f(1.0, src / canvas);\n}\n\n/** Canvas uv to source uv. */\nfn st_srcUv(uv : vec2f) -> vec2f {\n return (uv - 0.5) * st_srcScale() + 0.5;\n}\n\n/**\n * Canvas uv to source uv, with room left around the edge.\n *\n * A shader that displaces its reads runs off the source at the border, and the\n * sampler answers by smearing its last row of texels across the gap. Reading a\n * slightly smaller crop gives the displacement somewhere to go, at the cost of\n * a few per cent of the picture.\n */\nfn st_srcUvInset(uv : vec2f, margin : f32) -> vec2f {\n return (uv - 0.5) * st_srcScale() * (1.0 - clamp(margin, 0.0, 0.4)) + 0.5;\n}\n\n/**\n * A canvas-space displacement, in source uv.\n *\n * Divides out the aspect so a push of 0.01 moves the same distance sideways as\n * it does up, whatever shape the canvas or the source is.\n */\nfn st_srcWarp(offset : vec2f) -> vec2f {\n return offset * st_srcScale() / vec2f(st_aspect(), 1.0);\n}\n\n/** Samples in source uv. Kernels that walk neighbours want this one. */\nfn st_srcAt(srcUv : vec2f) -> vec4f {\n return textureSample(srcTexture, srcSampler, srcUv);\n}\n\n/** Samples in canvas uv — the ordinary read. */\nfn st_src(uv : vec2f) -> vec4f {\n return st_srcAt(st_srcUv(uv));\n}\n\nfn st_srcRgb(uv : vec2f) -> vec3f {\n return st_src(uv).rgb;\n}\n\n/** One source texel, in source uv. */\nfn st_srcTexel() -> vec2f {\n return 1.0 / max(source.size, vec2f(1.0));\n}\n\n/**\n * 1 inside the source, falling to 0 just outside it.\n *\n * The sampler clamps, so a shader that pushes past the border smears the last\n * row of texels across the gap. Anything that displaces should fade instead.\n */\nfn st_srcInside(srcUv : vec2f) -> f32 {\n let edge = min(srcUv, vec2f(1.0) - srcUv);\n return smoothstep(0.0, 0.004, min(edge.x, edge.y));\n}\n\n/** Four-tap softening. Cheap enough to run per colour channel. */\nfn st_srcSoften(uv : vec2f, radius : f32) -> vec3f {\n let base = st_srcUv(uv);\n var sum = vec3f(0.0);\n for (var i = 0; i < 4; i = i + 1) {\n let angle = f32(i) * 1.570796 + 0.785398;\n sum = sum + st_srcAt(base + st_srcWarp(vec2f(cos(angle), sin(angle)) * radius)).rgb;\n }\n return sum * 0.25;\n}\n\n/**\n * Thirteen-tap spiral blur. `radius` is in canvas space.\n *\n * The golden angle keeps the taps from lining up into spokes, which is what a\n * uniform ring blur gives you at low tap counts.\n */\nfn st_srcBlur(uv : vec2f, radius : f32) -> vec3f {\n let base = st_srcUv(uv);\n let taps = st_steps(12);\n var sum = st_srcAt(base).rgb;\n for (var i = 0; i < taps; i = i + 1) {\n let fi = f32(i);\n let angle = fi * 2.399963;\n let r = radius * sqrt((fi + 0.5) / f32(taps));\n sum = sum + st_srcAt(base + st_srcWarp(vec2f(cos(angle), sin(angle)) * r)).rgb;\n }\n return sum / f32(taps + 1);\n}\n\n// ---------------------------------------------------------------------------\n// swirl\n// ---------------------------------------------------------------------------\n\n// Swirl — a vortex that winds the layer around a point.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let aspect = st_aspect();\n let centre = params.pointer;\n let offset = (uv - centre) * vec2f(aspect, 1.0);\n let dist = length(offset);\n\n // Falloff decides the whole character: 1 is a gentle whirlpool, 4 is a tight\n // knot with still water around it.\n let radius = max(params.a, 0.02);\n let falloff = pow(clamp(1.0 - dist / radius, 0.0, 1.0), max(params.b, 0.1));\n let angle = falloff * params.warp * 4.0 + st_time() * params.c;\n\n // Rotation about the centre pushes the corners outside a rectangle, so the\n // read is inset enough to keep them on the picture.\n let toUv = vec2f(1.0 / aspect, 1.0);\n let twisted = st_rot(angle) * offset;\n let source = st_srcUvInset(twisted * toUv + centre, 0.06);\n\n // Winding the channels by slightly different angles smears colour along the\n // direction of travel, which is what sells the spin.\n let apart = params.d * 0.15 * falloff;\n let ahead = st_srcUvInset((st_rot(angle + apart) * offset) * toUv + centre, 0.06);\n let behind = st_srcUvInset((st_rot(angle - apart) * offset) * toUv + centre, 0.06);\n\n let col = vec3f(\n st_srcAt(ahead).r,\n st_srcAt(source).g,\n st_srcAt(behind).b,\n );\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
77
78
|
"tesla": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n // 1 on the High tier, 0 on Low. Read it through st_steps, not directly.\n quality : f32,\n e : f32,\n f : f32,\n g : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n // Appended so existing controls and colors keep their byte offsets.\n h : f32,\n i : f32,\n j : f32,\n k : f32,\n l : f32,\n m : f32,\n n : f32,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// The device-pixel size of one virtual cell, for pixel-art shaders. Cells\n// must be a whole number of device pixels: dividing the canvas by the\n// requested count directly leaves some cells one pixel fatter than others,\n// which the eye reads instantly as a broken grid. Use as:\n// let px = st_pixelSize(params.a);\n// let cell = floor(uv * params.resolution / px);\n// let puv = (cell + 0.5) * px / params.resolution;\nfn st_pixelSize(pixels : f32) -> f32 {\n return max(round(params.resolution.y / max(pixels, 8.0)), 1.0);\n}\n\n// --- quality ---------------------------------------------------------------\n// The runtime draws Low at a device pixel ratio of 1, which is most of the\n// saving. These are how a shader halves its own work per pixel as well, so\n// that the shaders whose cost is a loop — a march, a fan of rays, a spiral of\n// taps — cost half on Low and not merely a quarter of the pixels.\n\n// Whether the runtime asked for the cheaper picture.\nfn st_low() -> bool {\n return params.quality < 0.5;\n}\n\n// An iteration count for the current tier: n on High, about half on Low, and\n// never under three unless n itself is. Give it the bound of every loop whose\n// length is cost rather than look. A loop of three curtains or five layers is\n// the picture; a loop of ninety-six march steps is the price of it.\nfn st_steps(n : i32) -> i32 {\n if (!st_low()) { return n; }\n return max(i32(ceil(f32(n) * 0.5)), min(n, 3));\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n let count = st_steps(octaves);\n for (var i = 0; i < count; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// tesla\n// ---------------------------------------------------------------------------\n\n// Tesla — a plasma globe whose filament reaches for your finger.\n//\n// The bolts are analytic: each one is a base angle plus a noise wiggle that\n// grows with radius, and a pixel's distance to it is its angular error times\n// its radius. No marching, no segments — the whole filament costs two fbm\n// calls. The pointer is the interactive part: one bolt always aims at it,\n// clamped to the glass, which is exactly what the real toy does when a hand\n// touches the dome.\n\nfn wrapAngle(x : f32) -> f32 {\n return x - TAU * floor(x / TAU + 0.5);\n}\n\n// The glow one filament deposits at polar point (radius, theta).\nfn boltGlow(radius : f32, theta : f32, baseAngle : f32, t : f32, seed : f32, width : f32) -> vec2f {\n // The wiggle grows from nothing at the electrode, so every bolt is rooted\n // at the centre no matter how hard it thrashes at the rim.\n let sway = (st_fbm2(vec2f(radius * 3.5 - t * 2.2, seed * 43.7), 3) - 0.5)\n * params.warp * 2.4 * radius;\n let d = abs(wrapAngle(theta - baseAngle - sway)) * radius;\n\n // Core and halo, both against a width that thins as the bolt travels.\n let w = width * (0.5 + radius);\n let core = exp(-d / max(w * 0.012, 0.0005));\n let halo = exp(-d / max(w * 0.05, 0.002));\n return vec2f(core, halo);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n let p = st_centered(uv) / max(params.scale, 0.2);\n\n let R = 0.42;\n let radius = length(p);\n let theta = atan2(p.y, p.x);\n\n let cursor = vec2f((params.pointer.x - 0.5) * st_aspect(), params.pointer.y - 0.5)\n / max(params.scale, 0.2);\n\n let bolts = i32(clamp(params.a, 2.0, 8.0));\n var core = 0.0;\n var halo = 0.0;\n var tint = vec3f(0.0);\n\n for (var i = 0; i < bolts; i = i + 1) {\n let fi = f32(i);\n let seed = st_hash11(fi + 1.0);\n // Each bolt drifts around the globe at its own slow rate, and re-rolls\n // its heading on a coarse clock — the jump is what reads as electricity.\n let jump = floor(t * (1.5 + seed * 2.0) * params.c + seed * 9.0);\n let baseAngle = (fi + st_hash11(jump + fi * 17.0)) / f32(bolts) * TAU;\n let flicker = 0.55 + 0.45 * st_hash11(jump * 3.1 + fi);\n\n let g = boltGlow(radius, theta, baseAngle, t + seed * 8.0, seed, params.b);\n core = core + g.x * flicker;\n halo = halo + g.y * flicker;\n tint = tint + st_palette(seed) * g.y * flicker;\n }\n\n // The seeking bolt. It aims at the cursor, clamped to the inside of the\n // glass, and fades out when the cursor sits on the electrode where an\n // angle stops meaning anything.\n let cursorR = length(cursor);\n let aimR = min(cursorR, R - 0.015);\n let alive = smoothstep(0.06, 0.16, cursorR);\n // A pointer that has never moved reports the centre of the frame, which is\n // the electrode itself: atan2 has no answer there and Metal returns NaN.\n // The fade below cannot remove it, because NaN times zero is still NaN, and\n // one NaN blacks out the whole globe. Ask for the angle only where there is\n // one — the fade does the rest.\n let aim = select(0.0, atan2(cursor.y, cursor.x), cursorR > 1e-5);\n let g = boltGlow(radius, theta, aim, t, 0.77, params.b * 0.8);\n let reach = smoothstep(aimR + 0.06, aimR - 0.02, radius);\n core = core + g.x * 1.8 * alive * reach;\n halo = halo + g.y * 1.4 * alive * reach;\n tint = tint + st_palette(0.9) * g.y * alive * reach;\n\n // Contact bloom where the filament meets the glass or the finger.\n let touch = cursor / max(cursorR, 0.0001) * aimR;\n halo = halo + exp(-length(p - touch) * 22.0) * 1.6 * alive;\n\n // Assemble inside the globe: electrode, haze, filaments.\n let inside = smoothstep(R + 0.004, R - 0.004, radius);\n let electrode = exp(-radius * 55.0);\n var col = params.colorA.rgb * (0.05 + params.d * 0.12 * (1.0 - radius / R));\n col = col + tint * 0.3 + params.colorB.rgb * halo * 0.45;\n col = col + params.colorC.rgb * (core * 1.6 + electrode * 0.9);\n col = col * inside;\n\n // The glass: a thin rim, brightest where filaments are near it.\n let rim = exp(-abs(radius - R) * 120.0);\n col = col + params.colorB.rgb * rim * (0.15 + halo * 0.6);\n\n // A faint stand shadow below, so the globe sits on something.\n col = col + params.colorA.rgb * 0.06 * smoothstep(0.1, 0.5, p.y) * (1.0 - inside);\n\n return st_post(st_tonemap(col * params.intensity), uv);\n}\n",
|
|
@@ -150,6 +151,7 @@ export const shaderKinds = {
|
|
|
150
151
|
"silk": "generative",
|
|
151
152
|
"spectra": "graph",
|
|
152
153
|
"starfield": "generative",
|
|
154
|
+
"strata": "generative",
|
|
153
155
|
"swell": "generative",
|
|
154
156
|
"swirl": "source",
|
|
155
157
|
"tesla": "generative",
|
|
@@ -231,6 +233,7 @@ export const shaderReadsSource = {
|
|
|
231
233
|
"silk": false,
|
|
232
234
|
"spectra": false,
|
|
233
235
|
"starfield": false,
|
|
236
|
+
"strata": false,
|
|
234
237
|
"swell": false,
|
|
235
238
|
"swirl": true,
|
|
236
239
|
"tesla": false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/generated/sources.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,4EAA4E;AAC5E,oEAAoE;
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/generated/sources.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.d.ts
CHANGED
|
@@ -1096,6 +1096,185 @@ export declare const shaderRegistry: {
|
|
|
1096
1096
|
readonly colorC: ColorControl;
|
|
1097
1097
|
};
|
|
1098
1098
|
};
|
|
1099
|
+
readonly strata: {
|
|
1100
|
+
readonly name: "Strata";
|
|
1101
|
+
readonly tagline: "Colorful contour layers flowing around a deep violet basin";
|
|
1102
|
+
readonly description: "Nested ribbons of cyan, violet, coral and gold slowly bend around a recessed center. Soft shaded shelves and luminous lips give each contour depth. Adjust the layer count, relief and edge light, or change the three palette colors.";
|
|
1103
|
+
readonly family: "organic";
|
|
1104
|
+
readonly tags: readonly ["colorful", "layers", "contours", "flow", "hero", "ambient"];
|
|
1105
|
+
readonly cost: "light";
|
|
1106
|
+
readonly controls: {
|
|
1107
|
+
readonly speed: {
|
|
1108
|
+
readonly default: 0.45;
|
|
1109
|
+
readonly randomRange: readonly [0.2, 0.7];
|
|
1110
|
+
readonly slot: "speed";
|
|
1111
|
+
readonly label: "Speed";
|
|
1112
|
+
readonly min: 0;
|
|
1113
|
+
readonly max: 3;
|
|
1114
|
+
readonly step: 0.01;
|
|
1115
|
+
readonly hint: "Time multiplier. 0 freezes the shader on its current frame.";
|
|
1116
|
+
};
|
|
1117
|
+
readonly scale: {
|
|
1118
|
+
readonly randomRange: readonly [0.7, 1.12];
|
|
1119
|
+
readonly slot: "scale";
|
|
1120
|
+
readonly label: "Scale";
|
|
1121
|
+
readonly min: 0.2;
|
|
1122
|
+
readonly max: 3;
|
|
1123
|
+
readonly step: 0.01;
|
|
1124
|
+
readonly default: 1;
|
|
1125
|
+
readonly hint: "Zoom of the underlying field. Lower is bigger, softer shapes.";
|
|
1126
|
+
};
|
|
1127
|
+
readonly intensity: {
|
|
1128
|
+
readonly randomRange: readonly [0.85, 1.05];
|
|
1129
|
+
readonly slot: "intensity";
|
|
1130
|
+
readonly label: "Strength";
|
|
1131
|
+
readonly min: 0;
|
|
1132
|
+
readonly max: 2.5;
|
|
1133
|
+
readonly step: 0.01;
|
|
1134
|
+
readonly default: 1;
|
|
1135
|
+
readonly hint: "Overall brightness of the effect before the post chain.";
|
|
1136
|
+
};
|
|
1137
|
+
readonly vibrancy: {
|
|
1138
|
+
readonly randomRange: readonly [0.95, 1.15];
|
|
1139
|
+
readonly slot: "vibrancy";
|
|
1140
|
+
readonly label: "Vibrancy";
|
|
1141
|
+
readonly min: 0;
|
|
1142
|
+
readonly max: 2;
|
|
1143
|
+
readonly step: 0.01;
|
|
1144
|
+
readonly default: 1;
|
|
1145
|
+
readonly hint: "Saturation. 0 is greyscale, 1 is as authored, 2 is punched.";
|
|
1146
|
+
};
|
|
1147
|
+
readonly grain: {
|
|
1148
|
+
readonly randomRange: readonly [0, 0];
|
|
1149
|
+
readonly slot: "grain";
|
|
1150
|
+
readonly label: "Grain";
|
|
1151
|
+
readonly min: 0;
|
|
1152
|
+
readonly max: 0.35;
|
|
1153
|
+
readonly step: 0.005;
|
|
1154
|
+
readonly default: 0;
|
|
1155
|
+
readonly hint: "Per-pixel noise. A little hides banding on flat gradients.";
|
|
1156
|
+
};
|
|
1157
|
+
readonly vignette: {
|
|
1158
|
+
readonly default: 0.12;
|
|
1159
|
+
readonly randomRange: readonly [0.05, 0.22];
|
|
1160
|
+
readonly slot: "vignette";
|
|
1161
|
+
readonly label: "Vignette";
|
|
1162
|
+
readonly min: 0;
|
|
1163
|
+
readonly max: 1;
|
|
1164
|
+
readonly step: 0.01;
|
|
1165
|
+
readonly hint: "Darkening towards the edges of the frame.";
|
|
1166
|
+
};
|
|
1167
|
+
readonly warp: {
|
|
1168
|
+
readonly default: 1;
|
|
1169
|
+
readonly randomRange: readonly [0.45, 1.05];
|
|
1170
|
+
readonly hint: "How far the contour layers bend and flow.";
|
|
1171
|
+
readonly slot: "warp";
|
|
1172
|
+
readonly label: "Warp";
|
|
1173
|
+
readonly min: 0;
|
|
1174
|
+
readonly max: 2;
|
|
1175
|
+
readonly step: 0.01;
|
|
1176
|
+
};
|
|
1177
|
+
readonly layers: {
|
|
1178
|
+
readonly slot: "a";
|
|
1179
|
+
readonly label: "Layers";
|
|
1180
|
+
readonly min: 3;
|
|
1181
|
+
readonly max: 12;
|
|
1182
|
+
readonly step: 1;
|
|
1183
|
+
readonly default: 8;
|
|
1184
|
+
readonly randomRange: readonly [7, 12];
|
|
1185
|
+
readonly hint: "Number of nested shelves around the basin.";
|
|
1186
|
+
};
|
|
1187
|
+
readonly relief: {
|
|
1188
|
+
readonly slot: "b";
|
|
1189
|
+
readonly label: "Relief";
|
|
1190
|
+
readonly min: 0;
|
|
1191
|
+
readonly max: 1;
|
|
1192
|
+
readonly step: 0.01;
|
|
1193
|
+
readonly default: 0.85;
|
|
1194
|
+
readonly randomRange: readonly [0.8, 1];
|
|
1195
|
+
readonly hint: "Shadow depth across each shelf.";
|
|
1196
|
+
};
|
|
1197
|
+
readonly edgeLight: {
|
|
1198
|
+
readonly slot: "c";
|
|
1199
|
+
readonly label: "Edge light";
|
|
1200
|
+
readonly min: 0;
|
|
1201
|
+
readonly max: 2;
|
|
1202
|
+
readonly step: 0.01;
|
|
1203
|
+
readonly default: 1;
|
|
1204
|
+
readonly randomRange: readonly [0.7, 1.2];
|
|
1205
|
+
readonly hint: "Brightness of the rounded contour lips.";
|
|
1206
|
+
};
|
|
1207
|
+
readonly seed: {
|
|
1208
|
+
readonly slot: "d";
|
|
1209
|
+
readonly label: "Shape seed";
|
|
1210
|
+
readonly min: 0;
|
|
1211
|
+
readonly max: 99;
|
|
1212
|
+
readonly step: 1;
|
|
1213
|
+
readonly default: 0;
|
|
1214
|
+
readonly hint: "Changes the bends and proportions while keeping contours nested.";
|
|
1215
|
+
};
|
|
1216
|
+
readonly positionX: {
|
|
1217
|
+
readonly slot: "e";
|
|
1218
|
+
readonly label: "Depth point X";
|
|
1219
|
+
readonly min: -0.75;
|
|
1220
|
+
readonly max: 0.75;
|
|
1221
|
+
readonly step: 0.01;
|
|
1222
|
+
readonly default: 0;
|
|
1223
|
+
readonly randomRange: readonly [-0.45, 0.45];
|
|
1224
|
+
readonly hint: "Moves the depth point left or right. Zero centers it; ±0.5 reaches the frame edges. Layers follow the point.";
|
|
1225
|
+
};
|
|
1226
|
+
readonly positionY: {
|
|
1227
|
+
readonly slot: "f";
|
|
1228
|
+
readonly label: "Depth point Y";
|
|
1229
|
+
readonly min: -0.75;
|
|
1230
|
+
readonly max: 0.75;
|
|
1231
|
+
readonly step: 0.01;
|
|
1232
|
+
readonly default: 0;
|
|
1233
|
+
readonly randomRange: readonly [-0.45, 0.45];
|
|
1234
|
+
readonly hint: "Moves the depth point up or down. Negative moves up, positive moves down. Values past ±0.5 place it outside the frame.";
|
|
1235
|
+
};
|
|
1236
|
+
readonly rotation: {
|
|
1237
|
+
readonly slot: "g";
|
|
1238
|
+
readonly label: "Rotation";
|
|
1239
|
+
readonly min: -180;
|
|
1240
|
+
readonly max: 180;
|
|
1241
|
+
readonly step: 1;
|
|
1242
|
+
readonly default: 0;
|
|
1243
|
+
readonly hint: "Turns the contour composition around its basin.";
|
|
1244
|
+
};
|
|
1245
|
+
readonly stretch: {
|
|
1246
|
+
readonly slot: "h";
|
|
1247
|
+
readonly label: "Stretch";
|
|
1248
|
+
readonly min: 0.65;
|
|
1249
|
+
readonly max: 1.6;
|
|
1250
|
+
readonly step: 0.01;
|
|
1251
|
+
readonly default: 1;
|
|
1252
|
+
readonly randomRange: readonly [0.8, 1.4];
|
|
1253
|
+
readonly hint: "Changes round basins into elongated flowing valleys.";
|
|
1254
|
+
};
|
|
1255
|
+
};
|
|
1256
|
+
readonly colors: {
|
|
1257
|
+
readonly colorA: ColorControl;
|
|
1258
|
+
readonly colorB: ColorControl;
|
|
1259
|
+
readonly colorC: ColorControl;
|
|
1260
|
+
};
|
|
1261
|
+
readonly presets: readonly [{
|
|
1262
|
+
readonly name: "Prismatic";
|
|
1263
|
+
readonly colorA: "#5125db";
|
|
1264
|
+
readonly colorB: "#08d9ed";
|
|
1265
|
+
readonly colorC: "#ffb52e";
|
|
1266
|
+
}, {
|
|
1267
|
+
readonly name: "Lagoon";
|
|
1268
|
+
readonly colorA: "#123b89";
|
|
1269
|
+
readonly colorB: "#21ebd1";
|
|
1270
|
+
readonly colorC: "#b2f5e3";
|
|
1271
|
+
}, {
|
|
1272
|
+
readonly name: "Ember";
|
|
1273
|
+
readonly colorA: "#521b75";
|
|
1274
|
+
readonly colorB: "#f24878";
|
|
1275
|
+
readonly colorC: "#ffc65b";
|
|
1276
|
+
}];
|
|
1277
|
+
};
|
|
1099
1278
|
readonly spectra: {
|
|
1100
1279
|
readonly name: "Spectra";
|
|
1101
1280
|
readonly tagline: "Colour-split bands over a rippling field, swept by a slow pulse";
|
|
@@ -8782,7 +8961,7 @@ export declare const shaderRegistry: {
|
|
|
8782
8961
|
};
|
|
8783
8962
|
export type ShaderRegistry = typeof shaderRegistry;
|
|
8784
8963
|
export type ShaderName = keyof ShaderRegistry;
|
|
8785
|
-
export declare const shaderNames: ("alloy" | "archipelago" | "ascii" | "aurora" | "basalt" | "beacon" | "billow" | "bloom" | "blossom" | "blueprint" | "bokeh" | "caldera" | "caustics" | "chladni" | "cloth" | "cumulus" | "dither" | "dunes" | "duotone" | "edges" | "eyes" | "ferro" | "filament" | "film-grain" | "flow" | "fluid" | "glass" | "gossamer" | "gravity" | "halftone" | "hills" | "island" | "julia" | "kaleidoscope" | "lagoon" | "lens" | "liquid-light" | "liquid-metal" | "louvres" | "lumen" | "maelstrom" | "magnet" | "meadow" | "melt" | "mesh-gradient" | "mosaic" | "neon-grid" | "neon-sign" | "orb" | "parallax" | "particles" | "pixel-clouds" | "pixel-fire" | "pixel-isle" | "pixel-rain" | "pixel-sea" | "plasma" | "pulsar" | "radiance" | "reveal" | "ripple" | "shafts" | "shatter" | "shockwave" | "shore" | "silk" | "spectra" | "starfield" | "swell" | "swirl" | "tesla" | "torch" | "truchet" | "tunnel" | "voronoi")[];
|
|
8964
|
+
export declare const shaderNames: ("alloy" | "archipelago" | "ascii" | "aurora" | "basalt" | "beacon" | "billow" | "bloom" | "blossom" | "blueprint" | "bokeh" | "caldera" | "caustics" | "chladni" | "cloth" | "cumulus" | "dither" | "dunes" | "duotone" | "edges" | "eyes" | "ferro" | "filament" | "film-grain" | "flow" | "fluid" | "glass" | "gossamer" | "gravity" | "halftone" | "hills" | "island" | "julia" | "kaleidoscope" | "lagoon" | "lens" | "liquid-light" | "liquid-metal" | "louvres" | "lumen" | "maelstrom" | "magnet" | "meadow" | "melt" | "mesh-gradient" | "mosaic" | "neon-grid" | "neon-sign" | "orb" | "parallax" | "particles" | "pixel-clouds" | "pixel-fire" | "pixel-isle" | "pixel-rain" | "pixel-sea" | "plasma" | "pulsar" | "radiance" | "reveal" | "ripple" | "shafts" | "shatter" | "shockwave" | "shore" | "silk" | "spectra" | "starfield" | "strata" | "swell" | "swirl" | "tesla" | "torch" | "truchet" | "tunnel" | "voronoi")[];
|
|
8786
8965
|
export declare function getShader<K extends ShaderName>(name: K): ShaderRegistry[K];
|
|
8787
8966
|
export declare function isShaderName(value: string): value is ShaderName;
|
|
8788
8967
|
/**
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAoC,UAAU,EAAE,MAAM,YAAY,CAAC;AA+F7F,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAoC,UAAU,EAAE,MAAM,YAAY,CAAC;AA+F7F,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmmJ0B,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC;AACnD,MAAM,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC;AAE9C,eAAO,MAAM,WAAW,24BAEvB,CAAC;AAEF,wBAAgB,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAE1E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAIxE;AAED,iDAAiD;AACjD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAEpF,kEAAkE;AAClE,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,UAAU,IAAI,OAAO,CACtD,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EAAE,MAAM,CAAC,CAChF,CAAC"}
|
package/dist/registry.js
CHANGED
|
@@ -536,6 +536,42 @@ export const shaderRegistry = {
|
|
|
536
536
|
colorC: color("Core", "#ffffff", "What the crossings burn to."),
|
|
537
537
|
},
|
|
538
538
|
},
|
|
539
|
+
strata: {
|
|
540
|
+
name: "Strata",
|
|
541
|
+
tagline: "Colorful contour layers flowing around a deep violet basin",
|
|
542
|
+
description: "Nested ribbons of cyan, violet, coral and gold slowly bend around a recessed center. Soft shaded shelves and luminous lips give each contour depth. Adjust the layer count, relief and edge light, or change the three palette colors.",
|
|
543
|
+
family: "organic",
|
|
544
|
+
tags: ["colorful", "layers", "contours", "flow", "hero", "ambient"],
|
|
545
|
+
cost: "light",
|
|
546
|
+
controls: {
|
|
547
|
+
...base,
|
|
548
|
+
speed: { ...base.speed, default: 0.45, randomRange: [0.2, 0.7] },
|
|
549
|
+
scale: { ...base.scale, randomRange: [0.7, 1.12] },
|
|
550
|
+
intensity: { ...base.intensity, randomRange: [0.85, 1.05] },
|
|
551
|
+
vibrancy: { ...base.vibrancy, randomRange: [0.95, 1.15] },
|
|
552
|
+
grain: { ...base.grain, randomRange: [0, 0] },
|
|
553
|
+
vignette: { ...base.vignette, default: 0.12, randomRange: [0.05, 0.22] },
|
|
554
|
+
warp: { ...warp, default: 1, randomRange: [0.45, 1.05], hint: "How far the contour layers bend and flow." },
|
|
555
|
+
layers: { slot: "a", label: "Layers", min: 3, max: 12, step: 1, default: 8, randomRange: [7, 12], hint: "Number of nested shelves around the basin." },
|
|
556
|
+
relief: { slot: "b", label: "Relief", min: 0, max: 1, step: 0.01, default: 0.85, randomRange: [0.8, 1], hint: "Shadow depth across each shelf." },
|
|
557
|
+
edgeLight: { slot: "c", label: "Edge light", min: 0, max: 2, step: 0.01, default: 1, randomRange: [0.7, 1.2], hint: "Brightness of the rounded contour lips." },
|
|
558
|
+
seed: { slot: "d", label: "Shape seed", min: 0, max: 99, step: 1, default: 0, hint: "Changes the bends and proportions while keeping contours nested." },
|
|
559
|
+
positionX: { slot: "e", label: "Depth point X", min: -0.75, max: 0.75, step: 0.01, default: 0, randomRange: [-0.45, 0.45], hint: "Moves the depth point left or right. Zero centers it; ±0.5 reaches the frame edges. Layers follow the point." },
|
|
560
|
+
positionY: { slot: "f", label: "Depth point Y", min: -0.75, max: 0.75, step: 0.01, default: 0, randomRange: [-0.45, 0.45], hint: "Moves the depth point up or down. Negative moves up, positive moves down. Values past ±0.5 place it outside the frame." },
|
|
561
|
+
rotation: { slot: "g", label: "Rotation", min: -180, max: 180, step: 1, default: 0, hint: "Turns the contour composition around its basin." },
|
|
562
|
+
stretch: { slot: "h", label: "Stretch", min: 0.65, max: 1.6, step: 0.01, default: 1, randomRange: [0.8, 1.4], hint: "Changes round basins into elongated flowing valleys." },
|
|
563
|
+
},
|
|
564
|
+
colors: {
|
|
565
|
+
colorA: color("Basin", "#5125db", "The violet basin and inner layers."),
|
|
566
|
+
colorB: color("Cool ribbons", "#08d9ed", "The cyan side of the flowing shelves."),
|
|
567
|
+
colorC: color("Warm ribbons", "#ffb52e", "The gold end of the warm color ramp."),
|
|
568
|
+
},
|
|
569
|
+
presets: [
|
|
570
|
+
{ name: "Prismatic", colorA: "#5125db", colorB: "#08d9ed", colorC: "#ffb52e" },
|
|
571
|
+
{ name: "Lagoon", colorA: "#123b89", colorB: "#21ebd1", colorC: "#b2f5e3" },
|
|
572
|
+
{ name: "Ember", colorA: "#521b75", colorB: "#f24878", colorC: "#ffc65b" },
|
|
573
|
+
],
|
|
574
|
+
},
|
|
539
575
|
spectra: {
|
|
540
576
|
name: "Spectra",
|
|
541
577
|
tagline: "Colour-split bands over a rippling field, swept by a slow pulse",
|