import { Expr, GpuComputeNode, GpuFragmentParams } from '../../gpu/contract'; import { motionBlur as motionBlurKit, d } from '../../gpu/kit/index'; import { RttFilterParams } from '../../gpu/scaffolds/rttFilter'; import { ArgSpec } from '../invoke'; import { GatherEffect } from '../types'; import { PropRef } from '../values'; /** * The first stage of a stacked effect: reads the child as a texture and returns the starting * color at this pixel. * * Pass one to `gatherStack` as its first argument. The overlay stages then edit its result in order. * @see gatherStack, OverlayStage, childTap */ export type SampleStage = (params: RttFilterParams) => Expr; /** * One editing stage of a stacked effect: takes the color so far and returns the edited color. * * Stages run in the order you list them in `gatherStack`. * @see gatherStack, SampleStage */ export type OverlayStage = (color: Expr, params: RttFilterParams) => Expr; /** * Assemble an effect from one sampling stage and an ordered list of overlay stages. * * The child renders to a texture, the sampling stage reads it, and each overlay edits the result * in turn. Put the result in a filter's `effect:` field. Pass `{resultAlpha: 'straight'}` only * when the stack already returns straight (not premultiplied) alpha, as `crispTap` and * `refractedTaps` do. * * @example * ```ts * effect: gatherStack(rgbSplit({amount: p('colorShift')}), [scanlines({frequency: p('lines'), intensity: 0.3}), opaque()]) * ``` * @tip An empty overlay list is fine: `gatherStack(stage, [])` wraps a lone sampling stage. * @see SampleStage, OverlayStage, GatherFrame */ export declare function gatherStack(sample: SampleStage, overlays: OverlayStage[], opts?: { resultAlpha?: 'premultiplied' | 'straight'; }): GatherEffect; /** * A geometry that several stages of one effect share: a warped coordinate, a surface normal, a * coverage mask. * * Make one with a frame word (`sphereFrame`, `glitchFrame`, `fluteFrame`, `peelFrame`), keep it * in a module constant, and hand that same frame to every stage. It is computed once per pixel * however many stages read it. * @see sphereFrame, glitchFrame, fluteFrame, peelFrame */ export type GatherFrame = (params: RttFilterParams) => Expr; /** * The child with its red and blue pulled a little to either side, like a misaligned CRT. * * `amount` is the shift; 1 moves red and blue each by 0.002 of the canvas width, 0–10 is the * usual range. Returns a three-channel color, so finish the stack with `opaque`. * * @example * ```ts * effect: gatherStack(rgbSplit({amount: p('colorShift')}), [opaque()]) * ``` * @see glitchRgbSplit, scanlines, phosphorMask, opaque */ export declare function rgbSplit(opts: { amount: ArgSpec; }): SampleStage; /** * The wobbling, jittering coordinates of a worn VHS tape, to feed into `chromaSmearTaps`. * * `wobble` (0–5) is the tape damage: waves, creases and head-switching noise that burst on and * off over time. `jitter` (0–1) is fine per-scanline noise. `speed` scales the global clock, * so the definition needs no `animatedTime`. * * @example * ```ts * chromaSmearTaps({warp: tapeWarp({wobble: p('wobble'), jitter: p('scanlineNoise'), speed: p('speed')}), smear: p('smear')}) * ``` * @see chromaSmearTaps, beatPulse */ export declare function tapeWarp(opts: { wobble: ArgSpec; jitter: ArgSpec; speed: ArgSpec; }): (params: RttFilterParams) => Expr; /** * The child sampled like a VHS deck: brightness stays sharp while color bleeds sideways. * * `warp` is the coordinate set from `tapeWarp`. `smear` sets how far color trails; positive * trails it to the right, negative to the left, −2 to 2 is the usual range. Alpha comes from the * sharp tap. * * @example * ```ts * effect: gatherStack(chromaSmearTaps({warp: tapeWarp({wobble: p('wobble'), jitter: p('noise'), speed: p('speed')}), smear: p('smear')}), [beatPulse({wobble: p('wobble'), speed: p('speed')})]) * ``` * @see tapeWarp, beatPulse, rgbSplit */ export declare function chromaSmearTaps(opts: { warp: (params: RttFilterParams) => Expr; smear: ArgSpec; }): SampleStage; /** * The brightest color in a small ring around a point: the layer grown outward, so a spot * just outside a bright area still reads as bright. * * `read` samples the layer at a uv and returns straight color (pass `params.sampleStraight`). * `center` is the uv to look around; `radius` is the ring's radius per axis in uv, as a * `vec2` (divide a pixel radius by the viewport size). `taps` points sit evenly round the * ring starting at `angle` degrees, plus the centre itself. Returns the brightest of them as * `color` (straight) and its brightness as `luma` (Rec.709 luminance times alpha). * * @example * ```ts * const {color, luma} = brightestNear(params.sampleStraight, pointUV, {radius: div(p, viewport), taps: 4, angle: 45}) * ``` * @tip Thin bright details smaller than the gap between taps can slip through; more taps * close the gaps at one sample each. * @see gatherStack */ export declare function brightestNear(read: (uv: Expr) => Expr, center: Expr, opts: { radius: Expr; taps: number; angle?: number; }): { color: Expr; luma: Expr; }; /** * Brightness and contrast, pivoting around mid-grey. * * 1 leaves both unchanged; 0.5–2 is the usual range. Works on the three-channel color of a * stack begun with `rgbSplit`. * * @example * ```ts * adjust({brightness: p('brightness'), contrast: p('contrast')}) * ``` * @see rgbSplit, scanlines, opaque */ export declare function adjust(opts: { brightness: ArgSpec; contrast: ArgSpec; }): OverlayStage; /** * Horizontal scanlines darkening the child in a smooth wave. * * `frequency` is the number of lines from top to bottom (100–800). `intensity` (0–1) is how * dark the troughs get. Works on the three-channel color of a stack begun with `rgbSplit`. * * @example * ```ts * scanlines({frequency: p('scanlineFrequency'), intensity: p('scanlineIntensity')}) * ``` * @see distortedScanlines, phosphorMask, vignetteOverlay */ export declare function scanlines(opts: { frequency: ArgSpec; intensity: ArgSpec; }): OverlayStage; /** * A faint red, green and blue phosphor stripe pattern across the child. * * `pitch` sets how many stripes fit across the canvas, about half of `pitch`; 8–128 is the usual * range. Works on the three-channel color of a stack begun with `rgbSplit`. * * @example * ```ts * phosphorMask({pitch: p('pixelSize')}) * ``` * @see scanlines, rgbSplit */ export declare function phosphorMask(opts: { pitch: ArgSpec; }): OverlayStage; /** * Darkens the child toward its corners. * * `radius` (0–1) is how far the darkening reaches inward, 0 for the edges only and 1 for all the * way to the centre. `intensity` (0–1) blends it in. Round on any canvas shape. Works on the * three-channel color of a stack begun with `rgbSplit`. * * @example * ```ts * vignetteOverlay({radius: p('vignetteRadius'), intensity: p('vignetteIntensity')}) * ``` * @see scanlines, opaque */ export declare function vignetteOverlay(opts: { radius: ArgSpec; intensity: ArgSpec; }): OverlayStage; /** * Closes a three-channel stack as a solid color with alpha 1. * * Put it last after `rgbSplit`, `adjust`, `scanlines`, `phosphorMask` or `vignetteOverlay`, * which work on color only and drop the child's alpha. * * @example * ```ts * effect: gatherStack(rgbSplit({amount: 1}), [scanlines({frequency: 200, intensity: 0.3}), opaque()]) * ``` * @see rgbSplit, gatherStack */ export declare function opaque(): OverlayStage; /** * A faint brightness throb, like the mains hum of an old television. * * `wobble` scales the throb (0 turns it off). `speed` scales the global clock. Works on the * four-channel color of a stack begun with `chromaSmearTaps`. * * @example * ```ts * beatPulse({wobble: p('wobble'), speed: p('speed')}) * ``` * @see chromaSmearTaps, tapeWarp */ export declare function beatPulse(opts: { wobble: ArgSpec; speed: ArgSpec; }): OverlayStage; /** * The path a motion blur smears along: its kind plus the prop that anchors it. * * Make one with `blurPath.linear`, `blurPath.orbit` or `blurPath.zoom` and pass it to `motionBlur`. * @see blurPath, motionBlur */ export interface BlurPathSpec { readonly kind: motionBlurKit.MotionBlurPathKind; readonly focus: PropRef; } /** * The three paths `motionBlur` can smear along. * * @example * ```ts * effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity')}) * ``` * @see motionBlur */ export declare const blurPath: { /** * A straight streak along an angle prop, in degrees (0 points right, 90 points down). * @example * ```ts * effect: motionBlur({path: blurPath.linear(p('angle')), amount: p('intensity')}) * ``` * @see motionBlur */ linear: (angle: PropRef) => BlurPathSpec; /** * An arc streak rotating around a center prop (a position in uv). * @example * ```ts * effect: motionBlur({path: blurPath.orbit(p('center')), amount: p('intensity')}) * ``` * @see motionBlur */ orbit: (center: PropRef) => BlurPathSpec; /** * A radial streak rushing outward from a center prop (a position in uv). * @example * ```ts * effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity')}) * ``` * @see motionBlur */ zoom: (center: PropRef) => BlurPathSpec; }; /** * The optional lens and streak dials of `motionBlur`. Each is a prop ref whose value 0 is off; * give every one `recompile: crosses(0)` so the shader switches between the plain gather and * the detail gather exactly when a dial leaves or returns to 0. */ export interface MotionBlurDetailSlots { /** −1 to 1: tint the taps along the streak with a spectrum, red at one end and violet at the other; the sign swaps the ends. */ dispersion?: PropRef; /** 0–1: concentrate the dispersion away from the centre; 0 spreads it evenly. */ falloff?: PropRef; /** 0–1: a clean zone around the centre that the blur leaves untouched. */ focus?: PropRef; /** −1 to 1: trail the streak off one side of each pixel instead of both. */ bias?: PropRef; /** 0–1: let bright pixels streak further than dark ones. */ highlights?: PropRef; /** 0–1: dither the tap comb with per-pixel noise so long streaks do not band. */ jitter?: PropRef; /** −1 to 1, zoom paths only: corkscrew the streak around the centre. */ spiral?: PropRef; } /** * Smears the child along a path: a straight streak, an arc around a point, or a zoom out of a point. * * `amount` is a 0–100 intensity. A linear streak spans about twice `amount` in pixels; at 100 an * orbit sweeps about 29 degrees and a zoom pulls in from twice the distance to its center. * `detail` adds the lens dials (`dispersion`, `falloff`, `focus`) and streak dials (`bias`, * `highlights`, `jitter`, and `spiral` for a zoom); all are off at 0 and cost nothing until one * is set. * * @example * ```ts * effect: motionBlur({path: blurPath.linear(p('angle')), amount: p('intensity')}) * ``` * @example * ```ts * effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity'), detail: {dispersion: p('dispersion'), falloff: p('falloff'), focus: p('focus'), bias: p('bias'), highlights: p('highlights'), jitter: p('jitter'), spiral: p('spiral')}}) * ``` * @tip Samples past the canvas edge repeat the border color. Wrap the layer in a larger group if the smear must fade out instead. * @tip Give every `detail` prop `recompile: crosses(0)`; the detail gather is only compiled while one of them is non-zero. * @see blurPath, gaussianBlur, scatter */ export declare function motionBlur(opts: { path: BlurPathSpec; amount: ArgSpec; detail?: MotionBlurDetailSlots; }): GatherEffect; /** * Scatters every pixel of the child by a random offset: a grainy diffusion, not a smooth blur. * * `amount` is the largest offset in pixels. `edges` is a prop with `transform: transformEdges` * and `compileTime: true` ('stretch' | 'transparent' | 'mirror' | 'wrap') that says what to read * where an offset lands outside the canvas. `chromatic` (0–1, optional) scatters red and blue on * their own, for a sparkly colour grain; it is off at 0, so give it `recompile: crosses(0)`. * * @example * ```ts * effect: scatter({amount: p('intensity'), edges: p('edges')}) * ``` * @see gaussianBlur, motionBlur */ export declare function scatter(opts: { amount: ArgSpec; edges: PropRef; chromatic?: PropRef; }): GatherEffect; /** * Sharpens the child by boosting the contrast between each pixel and its neighbours. * * `amount` 0 leaves the child unchanged; 0–5 is the usual range. Alpha is untouched. * * @example * ```ts * effect: sharpen(p('sharpness')) * ``` * @tip Declare `identityWhen: isZero('sharpness')` and `recompile: crosses(0)` on the prop so 0 costs nothing. * @see gaussianBlur, scatter */ export declare function sharpen(amount: ArgSpec): GatherEffect; /** * Turns the child into a grid of flat cells, each showing one color. * * `scale` is the number of cells along the canvas's longer edge (1–200; more cells means smaller * pixels). `gap` (0–1) is the transparent space between cells as a fraction of a cell. * `roundness` (0–1) rounds each cell from a square to a circle. * * @example * ```ts * effect: pixelate({scale: p('scale'), gap: p('gap'), roundness: p('roundness')}) * ``` * @see scatter, sharpen */ export declare function pixelate(opts: { scale: ArgSpec; gap: ArgSpec; roundness: ArgSpec; }): GatherEffect; /** * The geometry of a sphere bulging out of the canvas: where each pixel reads the child from, * where the sphere ends, and which way its surface faces. * * `center` is a position in uv. `radius` 1 is half the canvas height. `depth` is how far it bulges * toward the viewer, 0 for flat and up to about 3. Feed the frame to `crispTap` and `rimLit`. * * @example * ```ts * const sphere = sphereFrame({center: p('center'), radius: p('radius'), depth: p('depth')}) * ``` * @see crispTap, rimLit, GatherFrame */ export declare function sphereFrame(opts: { center: ArgSpec; radius: ArgSpec; depth: ArgSpec; }): GatherFrame; /** * One clean sample of the child at a frame's warped coordinate. * * Use it under a warp that magnifies: hard edges in the child (text, a logo) stay smooth instead * of breaking into facets. Returns straight alpha, so close the stack with * `{resultAlpha: 'straight'}`. * * @example * ```ts * effect: gatherStack(crispTap(sphere), [rimLit(sphere, {position: p('light'), intensity: 0.5, softness: 0.5, color: p('lightColor')})], {resultAlpha: 'straight'}) * ``` * @see sphereFrame, rimLit, childTap */ export declare function crispTap(frame: GatherFrame): SampleStage; /** * A tinted rim of light around the edge of a bulged surface, added on top of the child's color. * * `position` is where the light sits, in uv. `intensity` (0–1) is its strength, 0 for off. * `softness` (0–1) runs from a hard edge to a soft glow. `color` is a color prop. Outside the * frame's surface the result is transparent. * * @example * ```ts * rimLit(sphere, {position: p('lightPosition'), intensity: p('lightIntensity'), softness: p('lightSoftness'), color: p('lightColor')}) * ``` * @see sphereFrame, crispTap, fluteHighlight */ export declare function rimLit(frame: GatherFrame, opts: { position: ArgSpec; intensity: ArgSpec; softness: ArgSpec; color: PropRef; }): OverlayStage; /** * The shared geometry of a digital glitch: when bursts fire, which horizontal bands jitter, and * which blocks shift or flip. * * `intensity` (0–1) sets both the strength and how often bursts happen. `speed` scales the global * clock, so no `animatedTime` is needed. `blockDensity` is the base number of bands (2–50). * `mirrorAmount` (0–1) is the chance a block shows flipped content. Keep the frame in a module * constant and pass it to `glitchRgbSplit`, `colorBarFills` and `distortedScanlines`. * * @example * ```ts * const glitch = glitchFrame({intensity: p('intensity'), speed: p('speed'), blockDensity: p('blockDensity'), mirrorAmount: p('mirrorAmount')}) * ``` * @see glitchRgbSplit, colorBarFills, distortedScanlines, GatherFrame */ export declare function glitchFrame(opts: { intensity: ArgSpec; speed: ArgSpec; blockDensity: ArgSpec; mirrorAmount: ArgSpec; }): GatherFrame; /** * The child sampled through a glitch frame, with red and blue pulled apart inside the active bands. * * `shift` is the split distance, 0–20. A transparent child stays transparent through the glitch. * * @example * ```ts * effect: gatherStack(glitchRgbSplit(glitch, {shift: p('rgbShift')}), [colorBarFills(glitch, {intensity: 0.2})]) * ``` * @see glitchFrame, colorBarFills, rgbSplit */ export declare function glitchRgbSplit(frame: GatherFrame, opts: { shift: ArgSpec; }): SampleStage; /** * Neon color bars filling the blocks a glitch frame currently has active. * * `intensity` (0–1) is how strongly the bars show. They respect the child's alpha. * * @example * ```ts * colorBarFills(glitch, {intensity: p('colorBarIntensity')}) * ``` * @see glitchFrame, glitchRgbSplit, distortedScanlines */ export declare function colorBarFills(frame: GatherFrame, opts: { intensity: ArgSpec; }): OverlayStage; /** * CRT-style scanlines that appear only where a glitch frame is currently distorting. * * `intensity` (0–1) is how visible they are. * * @example * ```ts * distortedScanlines(glitch, {intensity: p('scanlineIntensity')}) * ``` * @see glitchFrame, scanlines */ export declare function distortedScanlines(frame: GatherFrame, opts: { intensity: ArgSpec; }): OverlayStage; /** * The two halves a heavy blur adds to a definition: a pass that blurs the child ahead of time, * and the fragment that reads it. * * Spread the whole object into a definition that declares `species: 'custom'`, * `requiresRTT: true` and `requiresChild: true`. * * @example * ```ts * ...gaussianBlur({intensity: p('intensity')}) * ``` * @see gaussianBlur, channelBlur, progressiveBlur, tiltShift, bloom */ export interface ComputeBackedEffect { compute: GpuComputeNode; gpu: { fragment: (params: GpuFragmentParams) => Expr; }; } /** * A soft, even blur of the child. * * `intensity` is a prop from 0 to about 200; 100 is a blur radius of about 36 pixels. Bind the * prop to a map to vary the blur across the canvas. Only color blurs; the child's alpha stays * sharp, so soft edges need a group or mask around the layer. * * @example * ```ts * ...gaussianBlur({intensity: p('intensity')}) * ``` * @tip The blur runs at a fixed working resolution, so its cost does not grow with the canvas. * @see channelBlur, progressiveBlur, tiltShift, bloom, motionBlur */ export declare function gaussianBlur(opts: { intensity: PropRef; }): ComputeBackedEffect; /** * Blurs red, green and blue by separate amounts, for a soft chromatic fringe. * * Each of `red`, `green` and `blue` is a prop from 0 to 100; 100 is a radius of about 10 pixels. * A channel at 0 stays exactly sharp. * * @example * ```ts * ...channelBlur({red: p('redIntensity'), green: p('greenIntensity'), blue: p('blueIntensity')}) * ``` * @see gaussianBlur, rgbSplit */ export declare function channelBlur(opts: { red: PropRef; green: PropRef; blue: PropRef; }): ComputeBackedEffect; /** * The optional lens dials of `progressiveBlur` and `tiltShift`. `dispersion` (−1 to 1) blurs red * and blue by different radii for a defocus colour fringe that grows with the blur; `jitter` * (0–1) dithers the tap comb so long radii do not band. Both are off at 0 and read per frame; * give `dispersion` `recompile: crosses(0)`, since its kernel is only built while it is non-zero. */ export interface VariableBlurDetailSlots { dispersion?: PropRef; jitter?: PropRef; } /** * A blur that ramps from sharp to full strength in one direction across the child. * * The ramp starts at `center` (a position in uv) and runs along `angle` (degrees, 0 points * right), reaching full blur `falloff` (0–1, in uv) past the center. `intensity` (0–100) is the * blur at the far end; 100 is a radius of about 36 pixels. Bind `intensity` to a map to vary the * ceiling per pixel. Alpha stays sharp. `detail` adds the lens dials (see * `VariableBlurDetailSlots`), both off at 0. * * @example * ```ts * ...progressiveBlur({intensity: p('intensity'), angle: p('angle'), center: p('center'), falloff: p('falloff')}) * ``` * @see tiltShift, gaussianBlur */ export declare function progressiveBlur(opts: { intensity: PropRef; angle: PropRef; center: PropRef; falloff: PropRef; detail?: VariableBlurDetailSlots; }): ComputeBackedEffect; /** * Keeps a band of the child in focus and blurs everything beyond it, like a tilt-shift lens. * * The band runs through `center` (a position in uv) at `angle` (degrees, 0 is horizontal) and * is `width` wide (0–1, in uv). Beyond it the blur ramps to full strength over `falloff` (0–1). * `intensity` (0–100) is the blur at the far edges. Pixels inside the band stay pixel-sharp. * `detail` adds the lens dials (see `VariableBlurDetailSlots`), both off at 0. * * @example * ```ts * ...tiltShift({intensity: p('intensity'), width: p('width'), falloff: p('falloff'), angle: p('angle'), center: p('center')}) * ``` * @see progressiveBlur, gaussianBlur, bokehDefocus */ export declare function tiltShift(opts: { intensity: PropRef; width: PropRef; falloff: PropRef; angle: PropRef; center: PropRef; detail?: VariableBlurDetailSlots; }): ComputeBackedEffect; /** * A glow: the child's bright parts spread into a soft halo that adds back on top. * * Pixels brighter than `threshold` (0–1) glow. `size` is the halo's spread in pixels (clean up * to about 72). `intensity` (0–50) is how bright the halo is. The halo reaches past the child's * alpha, so a glowing shape lights up transparent space around it. At size 0 nothing runs; give * the size prop `recompile: crosses(0)`. * * @example * ```ts * ...bloom({intensity: p('intensity'), threshold: p('threshold'), size: p('size')}) * ``` * @see screenedBloom, gaussianBlur, bokehDefocus */ export declare function bloom(opts: { intensity: PropRef; threshold: PropRef; size: PropRef; }): ComputeBackedEffect; /** * The child's own color at this pixel, unchanged. * * The sampling stage for an effect that only adds something around the child, like a shadow. * * @example * ```ts * effect: gatherStack(childTap(), [shadowComposite({coverage, color: p('color'), intensity: p('intensity'), cutout: p('cutout')})]) * ``` * @see shadowComposite, crispTap */ export declare function childTap(): SampleStage; /** * The coordinate a shadow reads the child's silhouette from: `distance` away in the compass * direction `angle`. * * `angle` is in degrees, 0 up, 90 right, 180 down. `distance` (0–1) is a fraction of the canvas * height and covers the same number of pixels in either axis. Pass the result as the `at` of * `silhouetteCoverage`. * * @example * ```ts * silhouetteCoverage({at: shadowOffset({angle: p('angle'), distance: p('distance')}), blur: p('blur')}) * ``` * @see silhouetteCoverage, shadowComposite */ export declare function shadowOffset(opts: { angle: ArgSpec; distance: ArgSpec; }): (params: RttFilterParams) => Expr; /** * A soft copy of the child's alpha silhouette: a number per pixel from 0 to 1. * * `at` is where to read the silhouette from, usually `shadowOffset`. `blur` is the softness as a * radius in pixels (0–20). Feed it to `shadowComposite` as the `coverage`. * * @example * ```ts * silhouetteCoverage({at: shadowOffset({angle: 135, distance: 0.1}), blur: p('blur')}) * ``` * @see shadowOffset, shadowComposite */ export declare function silhouetteCoverage(opts: { at: (params: RttFilterParams) => Expr; blur: ArgSpec; }): (params: RttFilterParams) => Expr; /** * A tinted shadow behind the child. * * `coverage` is the soft silhouette from `silhouetteCoverage`, tinted with `color` (a color * prop) at `intensity` (0–1) and placed under the child. `cutout` is a boolean prop with * `compileTime: true`; when true only the shadow shows, with the child's shape punched out of it. * * @example * ```ts * effect: gatherStack(childTap(), [shadowComposite({coverage: silhouetteCoverage({at: shadowOffset({angle: p('angle'), distance: p('distance')}), blur: p('blur')}), color: p('color'), intensity: p('intensity'), cutout: p('cutout')})]) * ``` * @see childTap, shadowOffset, silhouetteCoverage */ export declare function shadowComposite(opts: { coverage: (params: RttFilterParams) => Expr; color: PropRef; intensity: ArgSpec; cutout: PropRef; }): OverlayStage; /** * The geometry of fluted (reeded) glass over the child: where each pixel reads through the * glass, how far its colors split, and the slope of the flute at that point. * * `shape` is a `compileTime` prop whose transform maps 'bars', 'rounded' and 'waves' to 0, 1 * and 2. `angle` is in degrees, 0 for vertical flutes. `frequency` is the number of flutes * across the longer edge (1–20). `softness` (0–1) eases each flute from flat-with-sharp-seams to * a gentle curve. `waveAmplitude` and `waveFrequency` shape the waves variant only. * `refraction` (0–4) is how hard each flute bends the child. `aberration` (0–1) is the color * split at the seams. The pattern drifts on the layer's clock, so declare * `animatedTime: {speed: 'speed'}`. Keep the frame in a module constant and pass it to * `refractedTaps` and `fluteHighlight`. * * @example * ```ts * const flute = fluteFrame({shape: p('shape'), angle: p('angle'), frequency: p('frequency'), softness: p('softness'), waveAmplitude: p('waveAmplitude'), waveFrequency: p('waveFrequency'), refraction: p('refraction'), aberration: p('aberration')}) * ``` * @see refractedTaps, fluteHighlight, GatherFrame */ export declare function fluteFrame(opts: { shape: PropRef; angle: ArgSpec; frequency: ArgSpec; softness: ArgSpec; waveAmplitude: ArgSpec; waveFrequency: ArgSpec; refraction: ArgSpec; aberration: ArgSpec; }): GatherFrame; /** * The child seen through a flute frame, with edge handling and a color split when `aberration` * is above 0. * * `edges` is a prop with `transform: transformEdges` and `compileTime: true`. `aberration` is * the same prop the frame uses; give it a recompile rule that fires when it crosses 0. Returns * straight alpha, so close the stack with `{resultAlpha: 'straight'}`. * * @example * ```ts * effect: gatherStack(refractedTaps(flute, {aberration: p('aberration'), edges: p('edges')}), [fluteHighlight(flute, {lightAngle: 30, highlight: 0.2, softness: 0.3, color: p('highlightColor')})], {resultAlpha: 'straight'}) * ``` * @see fluteFrame, fluteHighlight, crispTap */ export declare function refractedTaps(frame: GatherFrame, opts: { aberration: PropRef; edges: PropRef; }): SampleStage; /** * A specular highlight running along each flute, added on top of the child's color. * * `lightAngle` is the light's direction in degrees, 0 head-on and 90 grazing (−90 to 90). * `highlight` (0–2) is its strength. `softness` (0–1) spreads the peak from pin-tight to a * broad sheen. `color` is a color prop. Transparent parts of the child pick up no highlight. * * @example * ```ts * fluteHighlight(flute, {lightAngle: p('lightAngle'), highlight: p('highlight'), softness: p('highlightSoftness'), color: p('highlightColor')}) * ``` * @see fluteFrame, refractedTaps, rimLit */ export declare function fluteHighlight(frame: GatherFrame, opts: { lightAngle: ArgSpec; highlight: ArgSpec; softness: ArgSpec; color: PropRef; }): OverlayStage; /** * One shading term of a page peel: a number per pixel read against the peel frame. * * `curlShading`, `curlSheen`, `overhangShadow` and `revealShadow` each make one; `peelCompose` * takes all four. * @see peelCompose, peelFrame */ export type PeelShade = (params: RttFilterParams) => Expr; /** * The geometry of a page curling up from one corner: where the curl reads the child from, how * far it has turned, and how far each pixel is from the crease. * * `corner` is a `compileTime` string prop ('top-left' | 'top-right' | 'bottom-left' | * 'bottom-right') with no transform. `amount` (0–1) is how far the peel has progressed, 0 flat * and 1 fully peeled. `radius` is the tightness of the curl as a fraction of the page diagonal * (0.02–0.4). Keep the frame in a module constant and pass it to the peel shading words and * `peelCompose`. * * @example * ```ts * const peel = peelFrame({corner: p('corner'), amount: p('amount'), radius: p('radius')}) * ``` * @tip Leave the `corner` prop without a `transform`: it must stay a string for the frame to read. * @see peelCompose, curlShading, curlSheen, overhangShadow, revealShadow */ export declare function peelFrame(opts: { corner: PropRef; amount: ArgSpec; radius: ArgSpec; }): GatherFrame; /** * The darkening in the crook of a curl, easing to full brightness at the lip. * * `shading` (0–1) is how deep the darkening goes. * * @example * ```ts * curlShading(peel, {shading: p('shading')}) * ``` * @see peelFrame, peelCompose, curlSheen */ export declare function curlShading(frame: GatherFrame, opts: { shading: ArgSpec; }): PeelShade; /** * The band of light running up a curl. * * `highlight` (0–1) is its strength. `softness` (0–1) runs from a tight gloss to a broad satin. * * @example * ```ts * curlSheen(peel, {highlight: p('highlight'), softness: p('highlightSoftness')}) * ``` * @see peelFrame, peelCompose, curlShading */ export declare function curlSheen(frame: GatherFrame, opts: { highlight: ArgSpec; softness: ArgSpec; }): PeelShade; /** * The shadow a lifted curl casts on the flat page just past the crease. * * `amount` is the same peel amount the frame uses. `shadow` (0–1) is the shadow's strength. * * @example * ```ts * overhangShadow(peel, {amount: p('amount'), shadow: p('shadow')}) * ``` * @see peelFrame, peelCompose, revealShadow */ export declare function overhangShadow(frame: GatherFrame, opts: { amount: ArgSpec; shadow: ArgSpec; }): PeelShade; /** * The shadow on the surface a peel reveals, just inside the crease. * * `amount` is the same peel amount the frame uses. `shadow` (0–1) is the shadow's strength. * * @example * ```ts * revealShadow(peel, {amount: p('amount'), shadow: p('shadow')}) * ``` * @see peelFrame, peelCompose, overhangShadow */ export declare function revealShadow(frame: GatherFrame, opts: { amount: ArgSpec; shadow: ArgSpec; }): PeelShade; /** * A finished page peel: the cast shadows, the flat page and the lit curl, layered back to front. * * The sampling stage of a peel. Pass the four shading terms built on the same frame. Wrap it in * `gatherStack` with no overlays. * * @example * ```ts * effect: gatherStack(peelCompose(peel, {shade: curlShading(peel, {shading: 0.55}), sheen: curlSheen(peel, {highlight: 0.4, softness: 0.2}), overhang: overhangShadow(peel, {amount: p('amount'), shadow: 1}), reveal: revealShadow(peel, {amount: p('amount'), shadow: 1})}), []) * ``` * @see peelFrame, curlShading, curlSheen, overhangShadow, revealShadow */ export declare function peelCompose(frame: GatherFrame, parts: { shade: PeelShade; sheen: PeelShade; overhang: PeelShade; reveal: PeelShade; }): SampleStage; /** @internal */ export declare const mirrorRowUV: import('typegpu').TgpuFn<(uv: d.Vec2f, lineY: d.F32) => d.Vec2f>; /** @internal */ export declare const planarReflectionCompose: import('typegpu').TgpuFn<(original: d.Vec4f, refl: d.Vec4f, uvY: d.F32, lineY: d.F32, distance: d.F32, falloff: d.F32) => d.Vec4f>; /** @internal */ export declare function buildDepthRampFillGraph(computeWidth: number, computeHeight: number): { layout: import('typegpu').TgpuBindGroupLayout<{ blurMap: { storageTexture: d.WgslStorageTexture2d<"rgba32float", string>; }; params: { uniform: d.WgslStruct<{ height: d.F32; blurAmount: d.F32; blurDistance: d.F32; }>; }; }>; kernel: import('typegpu').TgpuFn<(cx: d.U32, cy: d.U32) => d.Void>; Params: d.WgslStruct<{ height: d.F32; blurAmount: d.F32; blurDistance: d.F32; }>; }; /** * The coordinate mirrored across a horizontal line, for reading a reflection below it. * * `line` is a prop, 0–1 down the canvas in uv. Returns a function of the fragment params that * gives the mirrored uv. Above the line the coordinate is still mirrored; `planarReflection` * only shows the reflection below it. * * @example * ```ts * const reflectedUV = mirrorAcrossRow(p('height')) * ``` * @see planarReflection, depthRampBlur */ export declare function mirrorAcrossRow(line: PropRef): (params: GpuFragmentParams) => Expr; /** * The blur half of a mirror floor: blurs the child more the further a row sits below the line. * * `line` is the same 0–1 line prop as `mirrorAcrossRow`. `blur` is the blur far below the line, * 0–5 where 1 is a radius of about 12 pixels. `blurDistance` (in uv, 0.01–1) is how far below * the line the blur takes to reach full strength. `halfKernel` is the number of taps each side * per pass; 30 is a good value. Spread it into a definition with `species: 'custom'` and read * the result in the fragment as `computeOutputs.blurredTexture`. * * @example * ```ts * ...depthRampBlur({line: p('height'), blur: p('blur'), blurDistance: p('blurDistance'), halfKernel: 30}) * ``` * @see planarReflection, mirrorAcrossRow, progressiveBlur */ export declare function depthRampBlur(opts: { line: PropRef; blur: PropRef; blurDistance: PropRef; halfKernel: number; }): { compute: GpuComputeNode; }; /** * A mirror floor: the child as it is above a line, and its reflection fading out below it. * * `line` (0–1 in uv), `distance` (how far below the line the reflection stays visible, in uv) * and `falloff` (the width of the fade as a fraction of `distance`) are props. `edges` is a prop * with `transform: transformEdges` and `compileTime: true`. `original` is the child's color at * this pixel, `reflection` samples the child (or its blurred copy) at a coordinate, and * `reflectedUV` is usually `mirrorAcrossRow(line)`. Returns a fragment function whose result is * premultiplied; unpremultiply it before returning from `gpu.fragment`. * * @example * ```ts * planarReflection({line: p('height'), distance: p('distance'), falloff: p('falloff'), edges: p('edges')}, source.sample(ctx.uv), (uv) => blurred.sample(uv), mirrorAcrossRow(p('height')))(params) * ``` * @see mirrorAcrossRow, depthRampBlur */ export declare function planarReflection(opts: { line: PropRef; distance: PropRef; falloff: PropRef; edges: PropRef; }, original: Expr, reflection: (uv: Expr) => Expr, reflectedUV: (params: GpuFragmentParams) => Expr): (params: GpuFragmentParams) => Expr; /** @internal */ export declare const tintedScreenGlow: import('typegpu').TgpuFn<(base: d.Vec4f, glow: d.Vec4f, tintStrength: d.Vec4f) => d.Vec4f>; /** * A tinted glow of the child's bright parts, screened over a color you have already graded: * film halation, phosphor bloom. * * `strength` is a prop; at 0 the glow pass does not run, so give it `recompile: crosses(0)`. * `radius` is a prop for the glow's spread in pixels. `tint` is an rgb triple (0–1) and * `threshold` (0–1) the brightness a pixel must pass to glow. `output` and `extractName` are * names unique to your definition. Returns `compute`, to set as the definition's `compute:`, and * `screen(base, at, params)`, which screens the glow at coordinate `at` over `base` in the * fragment. The glow reaches past the child's alpha. * * @example * ```ts * const halation = screenedBloom({strength: p('halation'), radius: p('halationRadius'), tint: [1, 0.38, 0.16], threshold: 0.62, output: 'halationTexture', extractName: 'myHalationExtract'}) * ``` * @tip Build it once at module level so the `compute:` field and the fragment read the same part. * @see bloom, frameSway */ export declare function screenedBloom(slots: { strength: PropRef; radius: PropRef; tint: [number, number, number]; threshold: number; output: string; extractName: string; }): { compute: GpuComputeNode; screen: (base: Expr, at: Expr, params: GpuFragmentParams) => Expr; }; /** @internal */ export declare const swayUV: import('typegpu').TgpuFn<(uv: d.Vec2f, amount: d.F32, t: d.F32) => d.Vec2f>; /** * A slight drifting shift and rotation of where the child is read from, like film in an * unsteady projector gate. * * `amount` is a prop from 0 to 1. `field` names an entry you declare in the definition's * `extraFields` as `{schema: schema.f32, initial: 0}`; the sway's clock lives there. Returns a * function of the fragment params that gives the uv to sample the child at. When `amount` drops * to 0 the motion freezes in place rather than snapping back. * * @example * ```ts * const weave = frameSway({amount: p('weave'), field: 'weaveTime'}) * ``` * @tip Build it once at module level and call it inside `gpu.fragment`; it registers its own per-frame clock. * @see screenedBloom */ export declare function frameSway(slots: { amount: PropRef; field: string; }): (params: GpuFragmentParams) => Expr; /** * A photographic lens blur where bright highlights bloom into aperture-shaped discs. * * `radius` (0–100) is the defocus; 100 is a disc about 80 pixels across. `gain` (0–10) is how * strongly highlights bloom and `threshold` (0–1) the brightness they must pass. `shape` is a * string prop: 'blades' | 'circle' | 'star' | 'heart' | 'flower' | 'cross' | 'ring'. `blades` * (0–9) counts blades or points for the blades, star and flower shapes; 0–2 is a round iris. * `rotation` is in degrees and `fringe` (0–1) adds color fringing at the disc edges. `catEye` * (0–1, optional) clips the discs into cat-eye shapes toward the frame corners, the way a lens * barrel vignettes its aperture; 0 is off. Both color and coverage blur, so silhouettes soften * like a real lens. Changing the aperture never recompiles. Spread it into a definition with * `species: 'custom'`, `requiresRTT: true` and `requiresChild: true`. * * @example * ```ts * ...bokehDefocus({radius: p('radius'), gain: p('highlightGain'), threshold: p('highlightThreshold'), shape: p('bladeShape'), blades: p('bladeCount'), rotation: p('bladeRotation'), fringe: p('chromaticFringe')}) * ``` * @see gaussianBlur, tiltShift, bloom */ export declare function bokehDefocus(opts: { radius: PropRef; gain: PropRef; threshold: PropRef; shape: PropRef; blades: PropRef; rotation: PropRef; fringe: PropRef; catEye?: PropRef; }): { compute: GpuComputeNode; gpu: { fragment: (params: GpuFragmentParams) => Expr; }; }; //# sourceMappingURL=blurs.d.ts.map