UNPKG

p5

Version:

[![npm version](https://badge.fury.io/js/p5.svg)](https://www.npmjs.com/package/p5)

202 lines (172 loc) 7.19 kB
import { ah as OPAQUE, ai as DILATE, aj as ERODE, ac as BLUR, ag as POSTERIZE, af as THRESHOLD, ak as INVERT, al as GRAY } from '../constants-DwbuOBz3.js'; /* * Creates p5.strands filter shaders for cross-platform compatibility. * * NOTE: These work a little differently than p5.js web editor shaders work! * Firstly, it uses instance mode, so we have to explicitly pass in context * variables in an argument to your callback and as a second argument to `modify`. * Secondly, always manually specify uniform names, as variable names will change * in minified builds. */ function makeFilterShader(renderer, operation, p5) { switch (operation) { case GRAY: return renderer.baseFilterShader().modify(({ p5 }) => { p5.getColor((inputs, canvasContent) => { const tex = p5.getTexture(canvasContent, inputs.texCoord); // weighted grayscale with luminance values const gray = p5.dot(tex.rgb, p5.vec3(0.2126, 0.7152, 0.0722)); return p5.vec4(gray, gray, gray, tex.a); }); }, { p5 }); case INVERT: return renderer.baseFilterShader().modify(({ p5 }) => { p5.getColor((inputs, canvasContent) => { const color = p5.getTexture(canvasContent, inputs.texCoord); const invertedColor = p5.vec3(1.0) - color.rgb; return p5.vec4(invertedColor, color.a); }); }, { p5 }); case THRESHOLD: return renderer.baseFilterShader().modify(({ p5 }) => { const filterParameter = p5.uniformFloat('filterParameter'); p5.getColor((inputs, canvasContent) => { const color = p5.getTexture(canvasContent, inputs.texCoord); // weighted grayscale with luminance values const gray = p5.dot(color.rgb, p5.vec3(0.2126, 0.7152, 0.0722)); const threshold = p5.floor(filterParameter * 255.0) / 255; const blackOrWhite = p5.step(threshold, gray); return p5.vec4(p5.vec3(blackOrWhite), color.a); }); }, { p5 }); case POSTERIZE: return renderer.baseFilterShader().modify(({ p5 }) => { const filterParameter = p5.uniformFloat('filterParameter'); const quantize = (color, n) => { // restrict values to N options/bins // and floor each channel to nearest value // // eg. when N = 5, values = 0.0, 0.25, 0.50, 0.75, 1.0 // then quantize (0.1, 0.7, 0.9) -> (0.0, 0.5, 1.0) color = color * n; color = p5.floor(color); color = color / (n - 1.0); return color; }; p5.getColor((inputs, canvasContent) => { const color = p5.getTexture(canvasContent, inputs.texCoord); const restrictedColor = quantize(color.rgb, filterParameter); return p5.vec4(restrictedColor, color.a); }); }, { p5 }); case BLUR: return renderer.baseFilterShader().modify(({ p5 }) => { const radius = p5.uniformFloat('radius'); const direction = p5.uniformVec2('direction'); // This isn't a real Gaussian weight, it's a quadratic weight const quadWeight = (x, e) => { return p5.pow(e - p5.abs(x), 2.0); }; const random = (p) => { let p3 = p5.fract(p.xyx * .1031); p3 += p5.dot(p3, p3.yzx + 33.33); return p5.fract((p3.x + p3.y) * p3.z); }; p5.getColor((inputs, canvasContent) => { const uv = inputs.texCoord; // A reasonable maximum number of samples const maxSamples = 64.0; let numSamples = p5.floor(radius * 7.0); if (p5.mod(numSamples, 2) == 0.0) { numSamples++; } let avg = p5.vec4(0.0); let total = 0.0; // Calculate the spacing to avoid skewing if numSamples > maxSamples let spacing = 1.0; if (numSamples > maxSamples) { spacing = numSamples / maxSamples; numSamples = maxSamples; } const randomOffset = (spacing - 1.0) * p5.mix(-0.5, 0.5, random(uv * inputs.canvasSize)); for (let i = 0; i < numSamples; i++) { const sample = i * spacing - (numSamples - 1.0) * 0.5 * spacing + randomOffset; const sampleCoord = uv + p5.vec2(sample, sample) / inputs.canvasSize * direction; const weight = quadWeight(sample, (numSamples - 1.0) * 0.5 * spacing); const texSample = p5.getTexture(canvasContent, sampleCoord); avg += weight * texSample * p5.vec4( texSample.a, texSample.a, texSample.a, 1 ); total += weight; } const blended = avg / total; return p5.vec4( blended.r / blended.a, blended.g / blended.a, blended.b / blended.a, blended.a ); }); }, { p5 }); case ERODE: return renderer.baseFilterShader().modify(({ p5 }) => { const luma = (color) => { return p5.dot(color.rgb, p5.vec3(0.2126, 0.7152, 0.0722)); }; p5.getColor((inputs, canvasContent) => { const uv = inputs.texCoord; let minColor = p5.getTexture(canvasContent, uv); let minLuma = luma(minColor); for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { if (x != 0 || y != 0) { const offset = p5.vec2(x, y) * inputs.texelSize; const neighborColor = p5.getTexture(canvasContent, uv + offset); const neighborLuma = luma(neighborColor); if (neighborLuma < minLuma) { minLuma = neighborLuma; minColor = neighborColor; } } } } return minColor; }); }, { p5 }); case DILATE: return renderer.baseFilterShader().modify(({ p5 }) => { const luma = (color) => { return p5.dot(color.rgb, p5.vec3(0.2126, 0.7152, 0.0722)); }; p5.getColor((inputs, canvasContent) => { const uv = inputs.texCoord; let maxColor = p5.getTexture(canvasContent, uv); let maxLuma = luma(maxColor); for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { if (x != 0 || y != 0) { const offset = p5.vec2(x, y) * inputs.texelSize; const neighborColor = p5.getTexture(canvasContent, uv + offset); const neighborLuma = luma(neighborColor); if (neighborLuma > maxLuma) { maxLuma = neighborLuma; maxColor = neighborColor; } } } } return maxColor; }); }, { p5 }); case OPAQUE: return renderer.baseFilterShader().modify(({ p5 }) => { p5.getColor((inputs, canvasContent) => { const color = p5.getTexture(canvasContent, inputs.texCoord); return p5.vec4(color.rgb, 1.0); }); }, { p5 }); default: throw new Error(`Unknown filter: ${operation}`); } } export { makeFilterShader };