From 71cf0b1efad73c9e2a55b781c6765040da17728d Mon Sep 17 00:00:00 2001 From: Stanislav Fifik Date: Fri, 12 Apr 2024 14:21:22 +0000 Subject: [PATCH] :sparkles: softedge --- src/nodes/WebGL/SoftEdge.ts | 152 ++++++++++++++++++++++++++++++++++++ src/nodes/index.ts | 1 + 2 files changed, 153 insertions(+) create mode 100644 src/nodes/WebGL/SoftEdge.ts diff --git a/src/nodes/WebGL/SoftEdge.ts b/src/nodes/WebGL/SoftEdge.ts new file mode 100644 index 0000000..9b5eba4 --- /dev/null +++ b/src/nodes/WebGL/SoftEdge.ts @@ -0,0 +1,152 @@ +import WebGL from './WebGl'; +import { + ImageVar, + NumberVar +} from '../io/AbstractIOSet'; + +const inputs = { + image: { + type: 'Image', + } as ImageVar, + radius: { + type: 'Number', + default: 8, + min: 0, + } as NumberVar, + sigma: { + type: 'Number', + default: 1, + step: 0.1, + min: 0, + } as NumberVar, + passes: { + type: 'Number', + default: 1, + } as NumberVar, +} + + +const outputs = { + image: { + type: 'Image', + } as ImageVar, + width: { + type: 'Number', + } as NumberVar, + height: { + type: 'Number', + } as NumberVar +} + +export default class SoftEdge extends WebGL { + + _lastRadiusValue?: number; + + constructor() { + super('SoftEdge', inputs) + } + + _passes() { + + const passes = Array(Math.max(this.in.passes.value, 0)) + .fill(null) + .flatMap((x, i) => { + return [ + (gl, program) => { + if (i === 0) { + this._setParams(gl, program) + } + this._setDirection(gl, program, [1, 0]) // Horizontal SoftEdge + }, + (gl, program) => { + this._setDirection(gl, program, [0, 1]) // Vertical SoftEdge + } + ] + }); + return passes; + } + + setup() { + if (this.in.radius.value !== this._lastRadiusValue) { + this.program = undefined; + } + super.setup(); + this._lastRadiusValue = this.in.radius.value; + } + + get frag() { + const RADIUS = Math.ceil(this.in.radius.value); + const KERNEL_SIZE = RADIUS + 1; + return ` + precision mediump float; + + const int RADIUS = ${RADIUS}; + // Our texture + uniform sampler2D u_image; + + uniform vec2 u_resolution; + uniform vec2 u_direction; + uniform vec2 u_textureSize; + + uniform float u_kernel[${KERNEL_SIZE}]; + + // The texCoords passed in from the vertex shader. + varying vec2 v_texCoord; + + void main() { + vec2 onePixel = vec2(1.0, 1.0) / u_textureSize; + + float totalWeight = u_kernel[0]; // Center weight + vec4 colorSum = texture2D(u_image, v_texCoord) * totalWeight; // center pixel + + for (int i = 1; i < ${KERNEL_SIZE}; i++) { + float weight = u_kernel[i]; + totalWeight += weight * 2.0; // Consider both sides of the kernel + vec4 color1 = texture2D(u_image, v_texCoord + onePixel * (u_direction * float(i))); + vec4 color2 = texture2D(u_image, v_texCoord - onePixel * (u_direction * float(i))); + colorSum += vec4((color1 + color2).rgb, 2.0 * min(color1.a, color2.a)) * weight; + } + + vec4 color = colorSum / totalWeight; + vec4 originalColor = texture2D(u_image, v_texCoord); + float alpha = min(color.a, texture2D(u_image, v_texCoord).a); + gl_FragColor = vec4(originalColor.rgb * alpha, alpha); + } + ` + } + + gaussian(x, sigma) { + const coeff = 1.0 / (2.0 * Math.PI * sigma * sigma); + const expon = -(x * x) / (2.0 * sigma * sigma); + return coeff * Math.exp(expon); + } + + createGaussianKernel(radius, sigma) { + const kernelSize = radius + 1; + const kernel = new Float32Array(kernelSize); + + for (let i = 0; i < kernelSize; i++) { + kernel[i] = this.gaussian(i, sigma); + } + + return kernel; + } + + + _setDirection(gl: WebGLRenderingContext, program: WebGLProgram, direction: [number, number]) { + gl.uniform2f(gl.getUniformLocation(program, 'u_direction'), direction[0], direction[1]); + } + + /** + * + * @param {WebGLRenderingContext} gl + * @param {*} program + */ + _setParams(gl, program) { + const kernel = this.createGaussianKernel( + this.in.radius.value, + this.in.sigma.value, + ) + gl.uniform1fv(gl.getUniformLocation(program, 'u_kernel'), kernel); + } +} \ No newline at end of file diff --git a/src/nodes/index.ts b/src/nodes/index.ts index 98ec11e..3fac825 100644 --- a/src/nodes/index.ts +++ b/src/nodes/index.ts @@ -17,6 +17,7 @@ export {default as GradientMap} from './WebGL/GradientMap'; export {default as ColorLookupTable} from './WebGL/ColorLookupTable'; export {default as Vignette} from './WebGL/Vignette'; export {default as Blur} from './WebGL/Blur'; +export {default as SoftEdge} from './WebGL/SoftEdge'; export {default as Edge} from './WebGL/Edge'; export {default as KernelPreset} from './WebGL/KernelPreset'; // Math