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 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);
+ * }
+ *
+ *