import { Expr, GpuFragmentParams } from '../../gpu/contract'; import { Field, Paint, Palette } from './fields'; import { PropRef } from '../values'; /** * One traveling sine wave, written as plain data. * * `x`, `y` and `t` are the wave's frequency along that axis: larger is tighter, and `t` is * its drift speed along the layer's clock. Leave an axis out to ignore it. A negative value * runs the wave the other way. `phase` shifts the wave, in radians. */ export interface Wave { x?: number; y?: number; t?: number; /** Radians, or `hashPhase(...)` for a seeded random phase. */ phase?: Expr | number; } /** * One term of a wave schedule: a single wave, or several multiplied together. * * Multiplying an x-wave by a y-wave gives an interference pattern instead of stripes. * `weight` scales the term, default 1. */ export interface WaveTerm { waves: Wave[]; weight?: number; } /** * Overlapping traveling sine waves from a schedule written as data: the organic wobble * behind blob edges, water lines and drifting fills. * * Pass the coordinates to wave over (`x`, optionally `y`, and `t` for the layer's clock) * and a list of terms. Each term multiplies its waves and the terms add up. The result is * signed: one wave runs −1 to 1, a schedule runs within about ±(sum of weights). It returns * an expression, not a field, so it slots into `paint: (params) => …` recipes and math. * * @example * ```ts * const wobble = waves({x: delta.member('x'), y: delta.member('y'), t: time}, [ * {waves: [{x: 3, t: 0.8}, {y: 2.2, t: -0.5}]}, * {waves: [{x: 5.1, y: -3.3, t: 0.6}], weight: 0.5}, * ]) * ``` * @tip Keep the schedule as constants next to the definition. It is the look, so keep it where you can tune it. * @see wavyLine, organicWaves, hashPhase */ export declare function waves(axes: { x: Expr | number; y?: Expr | number; t?: Expr | number; }, schedule: WaveTerm[]): Expr; /** * The height of an animated wavy line at a point along it: a stack of traveling sines. * * `along` is the coordinate along the line and `time` the layer's clock. Each wave has a * `freq` (tightness), a `speed` (drift along the clock), an `amount` (height) and an * optional `phase` in radians. Signed, within ±(sum of amounts). Add it to a baseline to get * the curve. Give each line of a family its own `hashPhase` so they do not move in step. * * @example * ```ts * const arc = wavyLine({along: x, time: t, waves: [{freq: 1.4, speed: 0.3, amount: 0.5}, {freq: 3.1, speed: -0.2, amount: 0.2}]}) * ``` * @see waves, hashPhase */ export declare function wavyLine(opts: { along: Expr | number; time: Expr | number; waves: { freq: number; speed: number; amount: number; phase?: Expr | number; }[]; }): Expr; /** * A ready-made `waves` schedule from three knobs, for when you have no hand-tuned constants. * * `frequency` sets the size of the largest waves. `detail` is how many finer layers stack on * top (default 3, each about 1.8× finer and half as strong). `drift` is the animation speed * (default 0.5). The same `seed` always gives the same schedule. Weights are normalized so * the summed wave stays within about −1 to 1. Treat the result as a starting point to tune. * * @example * ```ts * const wobble = waves({x: delta.member('x'), y: delta.member('y'), t: time}, organicWaves({frequency: 3, detail: 3, drift: 0.4})) * ``` * @tip The schedule uses x, y and t, so the axes you pass to `waves` must provide all three. * @see waves */ export declare function organicWaves(opts: { frequency: number; detail?: number; drift?: number; seed?: number; }): WaveTerm[]; /** * One ridge of a draped cloth as `foldSet` lays it out: where it rests across the drape, how * wide and how high it is, and the phase that keeps its ripple out of step with its neighbours. */ export interface Fold { offset: Expr; width: Expr; height: Expr; phase: Expr; } /** * Lays out the ridges of a draped cloth: `count` folds spread evenly across `spread`, each * nudged sideways, widened and raised by a seeded amount so no two are alike. * * `spread` is the distance the folds cover across the drape, in the units you will pass as * `across` to `drapedFolds` (the canvas aspect for a full-width curtain). `width` and * `height` are the typical ridge size. `jitter` (0–1, default 0.6) is how far a fold may * wander from its even slot. The same `seed` always gives the same layout. `count` is a plain * number, so drive it from a compile-time prop. * * @example * ```ts * const folds = foldSet({count: 7, seed: u.seed, spread: aspect, width: u.foldWidth, height: u.foldDepth}) * ``` * @tip Call it once, outside any height function: the layout is the same at every tap. * @see drapedFolds */ export declare function foldSet(opts: { count: number; seed: Expr | number; spread: Expr | number; width: Expr | number; height: Expr | number; jitter?: number; }): Fold[]; /** * The height of a draped cloth at a point: rounded ridges running along one direction, each * rippling and swaying along its length, gathered where the cloth is pinned. * * `coords` come from a `directionFrame`: `along` runs 0 at the pinned edge to 1 at the free * edge, `across` is the distance across the drape in the units `foldSet` laid the folds out * in. `ripple.waves` is a `wavyLine` wave list each spine wanders by (every fold starts at its * own phase) and `ripple.gain` scales it. `sway` moves all the folds together on the clock, * like a breeze. `swell` makes each ridge rise and fall along its length so neighbours merge * and part. `pinning` (0–1) gathers the folds at the pinned edge: narrow and still there, wide * and free at the far edge. `sharpness` (default 2) is how tightly the ridge falls away and * `crease` (0–1, default 0) flattens its crest into a plateau that meets the next fold in a * tight valley: 0 is a round bell, 1 a flat face with a sharp crease. The result is a height in * the units of each fold's `height`. Take its slope with `fdSlope` or `fdCurvature` for relief. * * @example * ```ts * const cloth = (at: Expr) => drapedFolds(drape.coordsOf(at), folds, {time: t, ripple: {waves: RIPPLE}, sway: {amount: breeze}, pinning: u.pinning}) * const relief = fdCurvature(cloth, q, 0.004) * ``` * @tip Keep the wave list and swell as constants next to the definition. They are the look. * @see foldSet, wavyLine, fdSlope */ export declare function drapedFolds(coords: { along: Expr | number; across: Expr | number; }, folds: Fold[], opts: DrapeOptions): Expr; /** * Where on the unfolded cloth a point lands: the material coordinates to draw a pattern in, * so it follows the folds instead of floating over them. * * Takes the same `coords`, `folds` and options as `drapedFolds`. The cloth riding each ridge * moves sideways with its spine, and each flank uses up cloth, so a pattern drawn in these * coordinates bends with the ripples and bunches on the steep sides of every ridge. * `foreshorten` (default 0.25) is how much cloth a flank consumes. Rebuild a point from the * result with the drape's frame and hand it to a grain word in place of the screen point. * * @example * ```ts * const m = clothCoords(drape.coordsOf(q), folds, {...drapeOpts, foreshorten: 0.25}) * const onCloth = add(mul(drape.tangent, m.along), mul(drape.perp, m.across)) * const threads = grainNoise(fibre.coordsOf(onCloth), {freq: [12, 420]}) * ``` * @tip One more pass over the folds per pixel. Call it once and reuse the point. * @see drapedFolds, foldSet, grainNoise */ export declare function clothCoords(coords: { along: Expr | number; across: Expr | number; }, folds: Fold[], opts: DrapeOptions & { foreshorten?: number; }): { along: Expr; across: Expr; }; /** The knobs that shape and animate a draped cloth, shared by `drapedFolds` and `clothCoords`. */ export interface DrapeOptions { time: Expr | number; /** A `wavyLine` wave list each spine wanders by (every fold starts at its own phase), and a gain on it. */ ripple: { waves: { freq: number; speed: number; amount: number; }[]; gain?: Expr | number; }; /** Moves all the folds together on the clock, like a breeze. */ sway?: { amount: Expr | number; rate?: number; }; /** Each ridge rises and falls along its length so neighbours merge and part. */ swell?: { amount: number; freq: number; speed?: number; }; /** 0–1: gathers the folds at the pinned edge. */ pinning?: Expr | number; /** How tightly a ridge falls away (default 2). */ sharpness?: number; /** 0–1 (default 0): flattens the crest into a plateau meeting the next fold in a tight valley. */ crease?: number; } /** * Frames a field on a zoomable plane: how big the pattern is and where it starts. * * Takes the canvas position, corrects it for aspect ratio, zooms it by `scale` and slides it * by `seed`, then hands a 2D pattern position to the field inside. `scale` is exponential: * each +1 makes the pattern about 2.7× finer, so a slider from −2 to 5 runs from coarse to * fine. A different `seed` shows a different part of the pattern. The framing every noise * texture opens with. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')})), stops(p('colorSpace'))) * ``` * @tip `noiseField` bases take a 3D position. Put `evolving` between `seededPlane` and the basis, even at `rate: 0`, to supply the third axis. * @see evolving, pixelGrid, scaledPlane */ export declare function seededPlane(field: Field, slots: { scale: PropRef; seed: PropRef; }): Field; /** * @internal Scratches' historical framing: `seededPlane` with an unguarded aspect divide, * kept so that shader's exact arithmetic is preserved. Authors use `seededPlane`. */ export declare function rawPlane(field: Field, slots: { scale: PropRef; seed: PropRef; }): Field; /** * Frames a field on the pixel grid: one value per block of `grain` device pixels. * * For patterns that live on pixels rather than on the canvas (blue noise, dither). `grain` * is the block size in pixels (1 = every pixel), `seed` picks a different block of the * pattern. The field inside gets whole-number pixel coordinates. * * @example * ```ts * paint: rampOver(noiseTone(pixelGrid(blueSpeckle(), {grain: p('grain'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see blueSpeckle, seededPlane */ export declare function pixelGrid(field: Field, slots: { grain: PropRef; seed: PropRef; }): Field; /** * Frames a field on the aspect-corrected canvas plane, zoomed by a plain multiplier. * * Unlike `seededPlane`, `scale` is linear: roughly the number of pattern cells across the * canvas height. There is no seed. The position is computed once, so a recipe whose parts * read it several times pays for it once. * * @example * ```ts * paint: rampOver(unitized(scaledPlane(waveletBands({detail: p('detail')}), {scale: p('scale')})), stops(p('colorSpace'))) * ``` * @see seededPlane, cellDistances */ export declare function scaledPlane(field: Field, slots: { scale: PropRef; }): Field; /** * Makes a 3D noise basis morph in place over time instead of sliding across the canvas. * * Wraps a basis that takes a 3D position (`noiseField`): the 2D pattern position from the * framing outside becomes `(x, y, time × rate)`. `rate` multiplies the layer's clock, on top * of the definition's `speed` prop. It sits between the framing and the basis. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(evolving(noiseField('perlin'), {rate: 0.3}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @tip The clock is the definition's `animatedTime: {speed: 'speed'}`. Without it nothing moves. * @see seededPlane, curlSpeed */ export declare function evolving(field: Field, opts: { rate: number; }): Field; /** * Oriented sine grains: short striped patches, each facing a random way, like brushed metal * or wood grain. * * `frequency` is how many stripes fit in one grain (1 to 24, default 8). The stripes slide * along their grain on the layer's clock. Signed, −1 to 1: wrap it in `unitized` before a * tone word. Takes a 2D position, so frame it with `seededPlane`. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see waveletBands, unitized */ export declare function gaborGrains(slots: { frequency: PropRef; }): Field; /** * Rotating bands of wavelets: layered ripples that turn against each other, a watery * interference look. * * `detail` is the frequency ratio between the four layers (1.05 to 2, default 1.24; higher * adds finer ripples). The bands slide on the layer's clock. Signed, about −1 to 1: wrap it * in `unitized` before a tone word. Takes a 2D position. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(unitized(waveletBands({detail: p('detail')})), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see gaborGrains, unitized */ export declare function waveletBands(slots: { detail: PropRef; }): Field; /** * Fine, even speckle with no clumps: blue noise, one random value per pixel block. * * Still (no animation). Unit range, about 0 to 1, so it feeds `noiseTone` directly. Only * makes sense over `pixelGrid`, which gives it the pixel coordinates it expects. * * @example * ```ts * paint: rampOver(noiseTone(pixelGrid(blueSpeckle(), {grain: p('grain'), seed: p('seed')})), stops(p('colorSpace'))) * ``` * @tip A good dither or film-grain layer over another shader at low opacity. * @see pixelGrid */ export declare function blueSpeckle(): Field; /** * Terrain eroded by water: sharp branching ridges with gullies running off them. * * Still (no animation). Signed, centred on 0: wrap it in `unitized` before a tone word. * Takes a 2D position, so frame it with `seededPlane`. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(unitized(erosionRidges()), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see unitized, seededPlane */ export declare function erosionRidges(): Field; /** * How fast a swirling flow moves at each point: soft eddies and streams that morph in place. * * `rate` multiplies the layer's clock. Never negative and mostly 0 to 1, so it feeds * `noiseTone` directly. Takes a 2D position, so frame it with `seededPlane`. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(curlSpeed({rate: 0.2}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see evolving, noiseTone */ export declare function curlSpeed(opts: { rate: number; }): Field; /** * Fine hairline scratches, like a scuffed surface, flickering on the layer's clock. * * `thickness` widens the bright core of each streak (0.2 to 5, default 1). The value is 0 * off a streak and rises well past 1 on one, so send it through `noiseTone`, which clamps. * Takes a 2D position. Works in a `paint:` only, not in a compute pass. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(scratchStreaks({thickness: p('thickness')}), {scale: p('scale'), seed: p('seed')})), linearPair(p('colorA'), p('colorB'))) * ``` * @see noiseTone, linearPair */ export declare function scratchStreaks(slots: { thickness: PropRef; }): Field; /** * Turns a signed field (−1 to 1) into a unit field (0 to 1). * * Put it directly around a signed basis (`gaborGrains`, `waveletBands`, `erosionRidges`, * `noiseField('mx3signed')`) so `noiseTone` and the palette see 0 to 1. Or skip it and use * `signedTone`, which takes the signed value itself. * * @example * ```ts * seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')}) * ``` * @see signedTone, noiseTone */ export declare function unitized(field: Field): Field; /** * The standard contrast and balance controls of a noise texture, turning a 0–1 field into * the ramp position. * * `contrast` steepens the field around mid-grey (0 = unchanged, positive = sharper, negative * = flatter). `balance` shifts the whole range (positive = more of the first color). Leave * the slots out for a texture without those controls. The result is flipped: where the * field is high the ramp shows its first color. Expects a unit field, so put `unitized` * around a signed basis first. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(evolving(noiseField('perlin'), {rate: 0.3}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @tip Contrast here is additive: 0 is the identity, not 1. Default the prop to 0. * @see signedTone, gainTone, unitized */ export declare function noiseTone(field: Field, slots?: { contrast?: PropRef; balance?: PropRef; }): Field; /** * Contrast and balance for a signed field (−1 to 1), applied before it is squashed to 0–1. * * The same controls as `noiseTone` (`contrast` 0 = unchanged, `balance` shifts), but working * on the raw signed value lets high contrast push further before it clips. Flipped like * `noiseTone`: a high field shows the first color. Use it in place of `unitized` plus * `noiseTone`. * * @example * ```ts * paint: rampOver(signedTone(seededPlane(evolving(noiseField('mx3signed'), {rate: 0.5}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see noiseTone, unitized */ export declare function signedTone(field: Field, slots: { contrast: PropRef; balance: PropRef; }): Field; /** * Contrast as a multiplier and balance as a shift over a 0–1 field, not flipped. * * `contrast` 1 leaves the field alone (a slider from 0.25 to 4 works well) and `balance` * shifts it. Low field values read the first color. The tone inside `worleyNoise`. Reach for * it when a "1 = normal" contrast slider fits better than `noiseTone`'s "0 = normal". * * @example * ```ts * paint: rampOver(gainTone(cellFill(cellDistances({scale: p('scale'), seed: p('seed')}), {edgeIntensity: p('edgeIntensity')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace'))) * ``` * @see noiseTone, worleyNoise */ export declare function gainTone(field: Field, slots: { contrast: PropRef; balance: PropRef; }): Field; /** * A two-color palette mixed in linear RGB, for a definition with no color-space prop. * * Reads two color props. Use `pair` or `stops` from paint when the definition has a * `colorSpace` prop. * * @example * ```ts * paint: rampOver(noiseTone(seededPlane(scratchStreaks({thickness: p('thickness')}), {scale: p('scale'), seed: p('seed')})), linearPair(p('colorA'), p('colorB'))) * ``` * @see pair, stops */ export declare function linearPair(a: PropRef, b: PropRef): Palette; /** * A complete fractal-noise paint: layered simplex noise that morphs over time, read through * the color ramp. * * `angle` rotates the pattern (degrees). `octaves` is how many layers stack (1 to 8, changed * live without a recompile), `detail` how much finer each layer is (1 to 4) and `contrast` * how much weaker (0.1 to 1). `seed` re-mixes the layers. `space` is the color-space prop. * Reads the definition's `colorA`, `colorB` and, when present, `stops` props. * * @example * ```ts * paint: fractalNoise({angle: p('angle'), detail: p('detail'), contrast: p('contrast'), octaves: p('octaves'), seed: p('seed'), space: p('colorSpace')}) * ``` * @tip Declare `animatedTime: {speed: 'speed'}` on the definition. The morphing runs on the layer's clock. * @see worleyNoise, noiseField */ export declare function fractalNoise(slots: { angle: PropRef; detail: PropRef; contrast: PropRef; octaves: PropRef; seed: PropRef; space: PropRef; }): Paint; /** * A complete cellular-noise paint: distances to drifting cell points, read through the * color ramp. * * `scale` is the number of cells across (1 to 30) and `jitter` how far the points wander * from a grid (0 = rigid grid, 1 = random). `mode` picks the distance field: `'f1'` * (cells), `'f2'`, `'f2MinusF1'` (edges), `'f1PlusF2'`, `'f1TimesF2'`. `distance` picks the * cell shape: `'euclidean'` (round), `'manhattan'` (diamond), `'chebyshev'` (square). * `octaves` (1 to 4) stacks finer layers, with `lacunarity` and `persistence` setting how * much finer and weaker. `contrast` is a multiplier (1 = unchanged) and `balance` a shift. * Low values read the first color. `mode`, `distance` and `octaves` are compile-time props. * Reads the definition's `colorA`, `colorB` and, when present, `stops` props. * * @example * ```ts * paint: worleyNoise({scale: p('scale'), jitter: p('jitter'), lacunarity: p('lacunarity'), persistence: p('persistence'), contrast: p('contrast'), balance: p('balance'), seed: p('seed'), mode: p('mode'), distance: p('distance'), octaves: p('octaves'), space: p('colorSpace')}) * ``` * @tip Give the `mode` and `distance` props `transformWorleyMode` and `transformWorleyDistance` as their `transform`. * @see cellDistances, transformWorleyMode, transformWorleyDistance, fractalNoise */ export declare function worleyNoise(slots: { scale: PropRef; jitter: PropRef; lacunarity: PropRef; persistence: PropRef; contrast: PropRef; balance: PropRef; seed: PropRef; /** Compile-time prop: `'f1' | 'f2' | 'f2MinusF1' | 'f1PlusF2' | 'f1TimesF2'`. */ mode: PropRef; /** Compile-time prop: `'euclidean' | 'manhattan' | 'chebyshev'`. */ distance: PropRef; /** Compile-time prop: 1 to 4. */ octaves: PropRef; space: PropRef; }): Paint; /** * Distances from each pixel to its nearest two cell points, as `(d1, d2)`: the raw material * for Voronoi fills and borders. * * `scale` is the number of cells across and `seed` picks a different set of points. The * points drift on the layer's clock. Feed it to `cellFill` or `cellBorders`, and wrap it in * `share` when both read it. * * @example * ```ts * const cells = share(cellDistances({scale: p('scale'), seed: p('seed')})) * ``` * @see cellFill, cellBorders, share */ export declare function cellDistances(slots: { scale: PropRef; seed: PropRef; }): Field; /** * A 0–1 fill for each cell: 0 at the cell's point, 1 at its boundary, whatever the cell size. * * `edgeIntensity` (0 to 1) is how far the boundary color reaches into the cell. Takes the * `(d1, d2)` field from `cellDistances`. Ramp it directly: the first color lands at the cell * centres. * * @example * ```ts * paint: rampOver(cellFill(cells, {edgeIntensity: p('edgeIntensity')}), stops(p('colorSpace'))) * ``` * @see cellDistances, cellBorders */ export declare function cellFill(cells: Field, slots: { edgeIntensity: PropRef; }): Field; /** * A mask of the lines between cells: 0 on a boundary, 1 inside a cell. * * `softness` is the line width (0 to about 0.4). Pass the same `scale` as `cellDistances` * so the lines keep their width when the cell count changes. Takes the `(d1, d2)` field. * Use it as the `mask` of `borderOverlay`. * * @example * ```ts * cellBorders(cells, {softness: p('edgeSoftness'), scale: p('scale')}) * ``` * @see cellDistances, borderOverlay */ export declare function cellBorders(cells: Field, slots: { softness: PropRef; scale: PropRef; }): Field; /** * Draws a color over a paint wherever a mask falls to 0, keeping the paint's alpha. * * `mask` is 1 where the paint shows through and 0 where `color` covers it. The Voronoi * border move: fill the cells, then overlay the border lines. * * @example * ```ts * paint: borderOverlay(rampOver(cellFill(cells, {edgeIntensity: p('edgeIntensity')}), stops(p('colorSpace'))), cellBorders(cells, {softness: p('edgeSoftness'), scale: p('scale')}), {color: p('colorBorder')}) * ``` * @see cellBorders, cellFill */ export declare function borderOverlay(paint: Paint, mask: Field, slots: { color: PropRef; }): Paint; /** A color-only step for `overRgb`: takes a paint's rgb and returns new rgb. Alpha is not touched. */ export type RgbStage = (rgb: Expr, params: GpuFragmentParams) => Expr; /** * Runs color-only steps over a paint, in order, leaving its alpha alone. * * This is how `ribbons` is closed: the ribbons come out in the working color space, * `backToP3` brings them to display color and `tonePow` lifts them. * * @example * ```ts * const strands = ribbons({from: p('start'), to: p('end'), count: p('lineCount'), width: p('lineWidth'), amplitude: p('amplitude'), frequency: p('frequency'), softness: p('softness'), spread: p('spread'), pinEdges: p('pinEdges'), colorScale: p('colorScale'), colorVariance: p('colorVariance')}) * paint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow()) * ``` * @see ribbons, backToP3, tonePow */ export declare function overRgb(paint: Paint, ...stages: RgbStage[]): Paint; /** * Brings `ribbons` color from its working color space back to display color. * * `space` is the definition's compile-time color-space prop; leave it out for linear. Put it * first in `overRgb`, right after the ribbons. * * @example * ```ts * paint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow()) * ``` * @see overRgb, ribbons */ export declare function backToP3(slots: { space?: PropRef; }): RgbStage; /** * A gentle lift of the mid-tones (rgb to the power 0.85), as an `overRgb` step. * * @example * ```ts * paint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow()) * ``` * @see overRgb */ export declare function tonePow(): RgbStage; /** * Flowing gradient-colored ribbons between two points: the Strands look. * * `from` and `to` are position props. `count` is how many ribbons, `width` and `softness` * their thickness and edge (0 to 1), `spread` how far apart they sit (0 to 1), `amplitude` * and `frequency` the size and tightness of the wave (0 to 5). `pinEdges` is a boolean prop * that gathers the ribbons at both ends. `colorScale` (1 to 6) is how many times the * gradient repeats along a ribbon and `colorVariance` (0 to 1) how far each ribbon is offset * in it. The gradient comes from the definition's `stops` prop. The color comes out in the * working color space, so close it with `overRgb(ribbons(…), backToP3({space}), tonePow())`. * The wave runs on `animatedTime: {speed: 'speed'}` and the colors scroll on a second clock, * `extraAnimatedTimes: {color: 'colorSpeed'}`. Declare both on the definition. * * @example * ```ts * paint: overRgb( * ribbons({from: p('start'), to: p('end'), count: p('lineCount'), width: p('lineWidth'), amplitude: p('amplitude'), frequency: p('frequency'), softness: p('softness'), spread: p('spread'), pinEdges: p('pinEdges'), colorScale: p('colorScale'), colorVariance: p('colorVariance')}), * backToP3({space: p('colorSpace')}), * tonePow(), * ) * ``` * @tip Add `stops: colorStopsPropConfig()` to the props. The ribbons read the stop list, not `colorA` / `colorB`. * @see overRgb, backToP3, tonePow */ export declare function ribbons(slots: { from: PropRef; to: PropRef; count: PropRef; width: PropRef; amplitude: PropRef; frequency: PropRef; softness: PropRef; spread: PropRef; pinEdges: PropRef; colorScale: PropRef; colorVariance: PropRef; }): Paint; /** A height field `noiseRelief` can emboss a layer with. Pick one from `reliefBases`. */ export interface ReliefBasis { /** The height function: a 2D position in, a height out. */ readonly fn: unknown; /** Its name in the compiled shader. */ readonly hint: string; } /** * The height fields `noiseRelief` accepts: `stone` (marbled stone) and `wool` (interwoven * fibres). * * @example * ```ts * gpu: {fragment: noiseRelief(reliefBases.wool)} * ``` * @see noiseRelief */ export declare const reliefBases: { /** Marbled stone height field. */ readonly stone: { readonly fn: import('typegpu').TgpuFn<(p: import('typegpu/data').Vec2f) => import('typegpu/data').F32>; readonly hint: "stone12"; }; /** Interwoven fibrous fabric height field. */ readonly wool: { readonly fn: import('typegpu').TgpuFn<(p: import('typegpu/data').Vec2f) => import('typegpu/data').F32>; readonly hint: "wool12"; }; }; /** * Embosses the layer inside with a textured surface: the child is slightly distorted, then * lit by a height field so it reads as carved stone or woven cloth. * * A filter, not a paint. Set it as the definition's fragment with `requiresRTT` and * `requiresChild`, and give the definition props named `intensity`, `scale`, `contrast`, * `distortion` and `seed`. `basis` is an entry of `reliefBases`. * * @example * ```ts * export const Stone = defineShader({ * name: 'Stone', * requiresRTT: true, * requiresChild: true, * props: {intensity: {default: 0.5}, scale: {default: 1}, contrast: {default: 0}, distortion: {default: 0.15}, seed: {default: 0}}, * gpu: {fragment: noiseRelief(reliefBases.stone)}, * }) * ``` * @tip The library shaders build those five props with `reliefStylizeProps` from the utilities, which also sets their sliders. * @see reliefBases */ export declare function noiseRelief(basis: ReliefBasis): (params: GpuFragmentParams) => Expr; /** * The `transform` for a `worleyNoise` `mode` prop: maps `'f1'`, `'f2'`, `'f2MinusF1'`, * `'f1PlusF2'` or `'f1TimesF2'` to the number the paint reads. * * @example * ```ts * mode: {default: 'f1', transform: transformWorleyMode, compileTime: true} * ``` * @see worleyNoise, transformWorleyDistance */ export declare const transformWorleyMode: (value: string) => number; /** * The `transform` for a `worleyNoise` `distance` prop: maps `'euclidean'`, `'manhattan'` or * `'chebyshev'` to the number the paint reads. * * @example * ```ts * distance: {default: 'euclidean', transform: transformWorleyDistance, compileTime: true} * ``` * @see worleyNoise, transformWorleyMode */ export declare const transformWorleyDistance: (value: string) => number; /** @internal The raw cell hash under WorleyNoise's historical name, re-exported for an external importer. */ export declare const worleyHash: import('typegpu').TgpuFn<(p: import('typegpu/data').Vec2f) => import('typegpu/data').Vec2f>; //# sourceMappingURL=noise.d.ts.map