diff --git a/src/strands/p5.strands.js b/src/strands/p5.strands.js index 7d212c332d..934bd0959a 100644 --- a/src/strands/p5.strands.js +++ b/src/strands/p5.strands.js @@ -30,7 +30,11 @@ function strands(p5, fn) { ////////////////////////////////////////////// // Global Runtime ////////////////////////////////////////////// - function initStrandsContext(ctx, backend, { active = false, renderer = null, baseShader = null } = {}) { + function initStrandsContext( + ctx, + backend, + { active = false, renderer = null, baseShader = null } = {}, + ) { ctx.dag = createDirectedAcyclicGraph(); ctx.cfg = createControlFlowGraph(); ctx.uniforms = []; @@ -78,11 +82,8 @@ function strands(p5, fn) { const prev = {}; for (const key of Object.getOwnPropertyNames(fn)) { - const descriptor = Object.getOwnPropertyDescriptor( - fn, - key - ); - if (descriptor && !descriptor.get && typeof fn[key] === 'function') { + const descriptor = Object.getOwnPropertyDescriptor(fn, key); + if (descriptor && !descriptor.get && typeof fn[key] === "function") { prev[key] = window[key]; window[key] = fn[key].bind(pInst); } @@ -104,7 +105,10 @@ function strands(p5, fn) { p5.Shader.prototype.modify = function (shaderModifier, scope = {}) { try { - if (shaderModifier instanceof Function || typeof shaderModifier === 'string') { + if ( + shaderModifier instanceof Function || + typeof shaderModifier === "string" + ) { // Reset the context object every time modify is called; // const backend = glslBackend; initStrandsContext(strandsContext, this._renderer.strandsBackend, { @@ -121,9 +125,10 @@ function strands(p5, fn) { if (options.parser) { // #7955 Wrap function declaration code in brackets so anonymous functions are not top level statements, which causes an error in acorn when parsing // https://github.com/acornjs/acorn/issues/1385 - const sourceString = typeof shaderModifier === 'string' - ? `(${shaderModifier})` - : `(${shaderModifier.toString()})`; + const sourceString = + typeof shaderModifier === "string" + ? `(${shaderModifier})` + : `(${shaderModifier.toString()})`; strandsCallback = transpileStrandsToJS( p5, sourceString, @@ -275,6 +280,97 @@ if (typeof p5 !== "undefined") { * */ +/** + * @method smoothstep + * @description + * A shader function that performs smooth Hermite interpolation between `0.0` + * and `1.0`. + * + * This function is equivalent to the GLSL built-in + * `smoothstep(edge0, edge1, x)` and is available inside p5.strands shader + * callbacks. It is commonly used to create soft transitions, smooth edges, + * fades, and anti-aliased effects. + * + * - Returns `0.0` when `x` is less than or equal to `edge0` + * - Returns `1.0` when `x` is greater than or equal to `edge1` + * - Smoothly interpolates between `0.0` and `1.0` when `x` is between them + * + * @param {Number} edge0 + * Lower edge of the transition + * @param {Number} edge1 + * Upper edge of the transition + * @param {Number} x + * Input value to interpolate + * + * @returns {Number} + * A value between `0.0` and `1.0` + * + * @example + *
+ * + * // Example 1: A soft vertical fade using smoothstep (no uniforms) + * + * let fadeShader; + * + * function fadeCallback() { + * getColor((inputs) => { + * // x goes from 0 → 1 across the canvas + * let x = inputs.texCoord.x; + * + * // smoothstep creates a soft transition instead of a hard edge + * let t = smoothstep(0.45, 0.55, x); + * + * // Use t directly as brightness + * return [t, t, t, 1]; + * }); + * } + * + * function setup() { + * createCanvas(300, 200, WEBGL); + * fadeShader = baseFilterShader().modify(fadeCallback); + * } + * + * function draw() { + * background(0); + * filter(fadeShader); + * } + * + *
+ * + * @example + *
+ * + * // Example 2: Animate the smooth transition using a uniform + * + * let animatedShader; + * + * function animatedFadeCallback() { + * const time = uniformFloat(() => millis() * 0.001); + * + * getColor((inputs) => { + * let x = inputs.texCoord.x; + * + * // Move the smoothstep band back and forth over time + * let center = 0.5 + 0.25 * sin(time); + * let t = smoothstep(center - 0.05, center + 0.05, x); + * + * return [t, t, t, 1]; + * }); + * } + * + * function setup() { + * createCanvas(300, 200, WEBGL); + * animatedShader = baseFilterShader().modify(animatedFadeCallback); + * } + * + * function draw() { + * background(0); + * filter(animatedShader); + * } + * + *
+ */ + /** * @method beforeVertex * @private