import type { TSLBoolNode, TSLFloatNode, TSLFunction, TSLIntNode, TSLVec3Node, TSLVec4Node } from '../types/tsl.cjs'; /** Scalar input accepted by the curl-noise controls. */ export type CurlFloatInput = TSLFloatNode | number; /** Integer input accepted by iteration controls. */ export type CurlIntInput = TSLIntNode | number; /** Boolean input accepted by branch controls. */ export type CurlBoolInput = TSLBoolNode | boolean; /** Arguments accepted by {@link snoise3d_deriv}. */ export type SimplexNoiseDerivativeArguments = [position: TSLVec3Node]; /** Arguments accepted by {@link curlNoise}. */ export type CurlNoiseArguments = [ position: TSLVec3Node, frequency: CurlFloatInput, time: CurlFloatInput, amplitude: CurlFloatInput, normalize: CurlBoolInput ]; /** Arguments accepted by {@link curlNoiseFbm}. */ export type CurlNoiseFbmArguments = [ position: TSLVec3Node, baseFrequency: CurlFloatInput, time: CurlFloatInput, octaves: CurlIntInput, lacunarity: CurlFloatInput, gain: CurlFloatInput, amplitude: CurlFloatInput, normalize: CurlBoolInput ]; /** Arguments accepted by {@link curlAdvect}. */ export type CurlAdvectArguments = [ position: TSLVec3Node, frequency: CurlFloatInput, time: CurlFloatInput, steps: CurlIntInput, dt: CurlFloatInput, warp: CurlFloatInput, amplitude: CurlFloatInput, useFbm: CurlBoolInput, octaves: CurlIntInput, lacunarity: CurlFloatInput, gain: CurlFloatInput ]; /** * * **What is curl noise?** * Curl noise constructs a *vector field* by taking the curl of a vector potential: * `flow = ∇ × A`. Because `div(∇ × A) = 0`, the resulting field is divergence-free. * * **Usual usage** * - Advect particles: `pos += curlNoise(pos) * dt` (or integrate multiple substeps for prettier flow) * - UV/normal distortion: flow-based warping without obvious compress/expand artifacts * - Stylized fluid-like motion and turbulence fields * * **Performance notes** * - Uses analytic derivatives of simplex noise ⇒ **3 noise evaluations** per sample * - Avoids finite differences ⇒ typically far faster than naive curl implementations * * **Code example** * ```js * const positionBuffer = instancedArray(COUNT, 'vec3'); * const velocityBuffer = instancedArray(COUNT, 'vec3'); * const prevVelocityBuffer = instancedArray(COUNT, 'vec3'); * const matrixBuffer = instancedArray(COUNT, 'mat4'); * const aoBuffer = instancedArray(COUNT, 'float'); * * // Build lookAt matrix from direction (reusable in compute) * const buildLookAtMatrix = Fn(([direction, position]) => { * const up = vec3(0, 1, 0); * const dir = normalize(direction); * * const right = normalize(cross(up, dir)); * const correctedUp = normalize(cross(dir, right)); * * // Build full transformation matrix with rotation + translation * return mat4( * vec4(right, 0), * vec4(correctedUp, 0), * vec4(dir, 0), * vec4(position, 1) * ); * }); * * // computeInit: spawn particles at center with curl noise distribution * const computeInit = Fn(() => { * const i = float(instanceIndex); * * // Spherical distribution from center * const phi = i.mul(2.399963); // golden angle * const theta = i.div(COUNT).mul(Math.PI); * const r = i.div(COUNT).sqrt().mul(3.0); * * const pos = vec3( * r.mul(theta.sin()).mul(phi.cos()), * r.mul(theta.cos()), * r.mul(theta.sin()).mul(phi.sin()) * ); * * const initVel = vec3(0, 1, 0); * * positionBuffer.element(instanceIndex).assign(pos); * velocityBuffer.element(instanceIndex).assign(initVel); * prevVelocityBuffer.element(instanceIndex).assign(initVel); * matrixBuffer.element(instanceIndex).assign(buildLookAtMatrix(initVel, pos)); * aoBuffer.element(instanceIndex).assign(0.0); * }); * * renderer.compute(computeInit().compute(COUNT)); * * // Uniforms * const uFrequency = uniform(FREQUENCY); * const uAmplitude = uniform(AMPLITUDE); * const uSpeed = uniform(SPEED); * const uSteps = uniform(STEPS); * const uDt = uniform(DT); * * // computeUpdate: advect with curl noise, compute mat4 * const computeUpdateFn = Fn(() => { * const pos = positionBuffer.element(instanceIndex).toVar(); * const vel = velocityBuffer.element(instanceIndex); * const prevVel = prevVelocityBuffer.element(instanceIndex); * * // Store previous velocity before computing new one * prevVel.assign(vel); * * // Oscillating scale for grow/shrink effect * const scale = sin(time.mul(uSpeed)).mul(0.5).add(1.0); * const dt = float(uDt).mul(scale); * * // Advect through curl noise field * Loop(int(uSteps), () => { * const v = curlNoise(pos, uFrequency, time.mul(0.05), uAmplitude, false); * pos.addAssign(v.mul(dt)); * }); * * // Store current velocity * vel.assign(pos.sub(positionBuffer.element(instanceIndex))); * * // Smooth direction from mix of prev and current velocity * const smoothDir = normalize(mix(prevVel, vel, 0.5).add(vec3(0.0001))); * * // Compute and store mat4 * matrixBuffer.element(instanceIndex).assign(buildLookAtMatrix(smoothDir, pos)); * * // Fake AO based on multiple factors: * // 1. Distance from center (darker at center where particles cluster) * const distFromCenter = length(pos); * const centerAo = float(1.0).sub(clamp(distFromCenter.div(5.0), 0.0, 1.0)); * * // 2. Vertical position (darker at bottom) * const heightAo = clamp(pos.z.div(4.8).add(0.5), 0.0, 1.0); * * // 3. Velocity magnitude (slower = more clustered = darker) * const speed = length(vel); * const speedAo = float(1.0).sub(clamp(speed.mul(50.0), 0.0, 1.0)); * * // Combine AO factors * const ao = clamp(centerAo.oneMinus().mul(1).add(heightAo.mul(0.3)).add(speedAo.mul(0.3)), 0.0, 1.0); * aoBuffer.element(instanceIndex).assign(ao); * * positionBuffer.element(instanceIndex).assign(pos); * }); * * computeUpdate = computeUpdateFn().compute(COUNT); * * // Material with direction-based coloring * const material = new THREE.MeshPhysicalNodeMaterial({ * metalness: 0.8, * roughness: 0.3 * }); * * const vel = velocityBuffer.element(instanceIndex); * const prevVel = prevVelocityBuffer.element(instanceIndex); * * // Color based on velocity direction * const smoothDir = normalize(mix(prevVel, vel, 0.5).add(vec3(0.0001))); * const colX = abs(smoothDir.x); * const colY = abs(smoothDir.y); * const colZ = abs(smoothDir.z); * * const cA = color(0x00ffff); // cyan * const cB = color(0xff00ff); // magenta * const cC = color(0xffff00); // yellow * * material.colorNode = mix(mix(cA, cB, colX), cC, colY.add(colZ).mul(0.5)); * * // Apply fake AO from buffer * material.aoNode = aoBuffer.element(instanceIndex); * * // Mesh with small box geometry * const geometry = new THREE.BoxGeometry(0.08, 0.28, 0.08); * const mesh = new THREE.InstancedMesh(geometry, material, COUNT); * * // Use mat4 buffer directly for instance matrices * mesh.instanceMatrix = matrixBuffer.value; * scene.add(mesh); * ``` * * @module CurlNoise * @short TSL curl-noise utilities for divergence-free flow fields, FBM variants, and advection helpers. * @category TSL * @tags WebGPU, WebGL * @demo docs/demos/curlnoise.html */ /** * @typedef {Object} CurlNoiseOptions * @memberof CurlNoise * @property {number} [frequency=1.0] Spatial frequency (detail scale). Higher = finer swirls. * @property {number} [time=0.0] Animation time. Implemented as a cheap translation in noise space. * @property {number} [amplitude=1.0] Overall magnitude of the returned flow. * @property {boolean} [normalize=false] When true, returns a unit-length direction (extra cost). * @property {number} [octaves=3] (FBM) Number of octaves. Typical: 2–4 for realtime. * @property {number} [lacunarity=2.0] (FBM) Frequency multiplier per octave. * @property {number} [gain=0.5] (FBM) Amplitude multiplier per octave. */ /** * Analytic 3D simplex noise with derivative. * * - Returns `vec4( dndx, dndy, dndz, n )` * - `n` is roughly in `[-1, +1]` * - The `.xyz` derivative is what makes curl noise *fast* (no finite differences needed). * * @function snoise3d_deriv * @param {vec3} p 3D position in noise space. * @private * @returns {vec4} vec4( dndx, dndy, dndz, n ) */ export declare const snoise3d_deriv: TSLFunction; /** * Divergence-free curl noise vector field (incompressible swirling flow). * * **Note** * This function returns a velocity field. For “pretty curl-noise visuals”, integrate it * over time (see `curlAdvect`) or use it to advect particles. * * @function curlNoise * @tags WebGPU, WebGL * @tsl * @param {vec3} position World/texture position. * @param {number|float} [frequency=1.0] Spatial frequency (detail scale). * @param {number|float} [time=0.0] Animation time (translation in noise space). * @param {number|float} [amplitude=1.0] Output magnitude. * @param {boolean|bool} [normalize=false] When true, output is unit length (direction only). * @returns {vec3} Divergence-free flow vector. */ export declare const curlNoise: TSLFunction; /** * Multi-octave curl noise (FBM) for richer turbulence. * * Keep octaves low for realtime (2–4). Each octave is still efficient * (3 derivative-noise evaluations), but it adds up quickly. * * @function curlNoiseFbm * @tags WebGPU, WebGL * @tsl * @param {vec3} position * @param {number|float} [baseFrequency=1.0] * @param {number|float} [time=0.0] * @param {number|int} [octaves=3] * @param {number|float} [lacunarity=2.0] * @param {number|float} [gain=0.5] * @param {number|float} [amplitude=1.0] * @param {boolean|bool} [normalize=false] * @returns {vec3} */ export declare const curlNoiseFbm: TSLFunction; /** * Integrate (advect) a position through a curl-noise field to reveal coherent vortices. * * This is the “missing piece” when demos look like generic displacement. Advection performs: * `p = p + v(p) * dt` repeatedly. Even 4–8 steps can transform the look dramatically. * * **Design choices** * - The field is kept mostly stable by using a very small internal time drift. * - A cheap domain warp breaks axis-aligned / lattice artifacts (especially in instanced grids). * * @function curlAdvect * @tags WebGPU, WebGL * @tsl * @param {vec3} position Starting position. * @param {number|float} [frequency=1.0] Field frequency (detail scale). * @param {number|float} [time=0.0] Advection time driver (used for subtle drift). * @param {number|int} [steps=6] Number of substeps (4–8 typical). * @param {number|float} [dt=0.18] Step size per substep (0.10–0.25 typical). * @param {number|float} [warp=0.75] Domain warp strength (0 disables). * @param {number|float} [amplitude=1.0] Scales final displacement from the original position. * @param {boolean|bool} [useFbm=true] Uses FBM curl field when true, single-octave when false. * @param {number|int} [octaves=3] FBM octaves (used if useFbm=true). * @param {number|float} [lacunarity=2.0] FBM lacunarity. * @param {number|float} [gain=0.5] FBM gain. * @returns {vec3} Advected position. */ export declare const curlAdvect: TSLFunction;