diff --git a/src/nodes/WebGL/Blur.js b/src/nodes/WebGL/Blur.js new file mode 100644 index 0000000..5c31bad --- /dev/null +++ b/src/nodes/WebGL/Blur.js @@ -0,0 +1,82 @@ +import WebGL from './WebGl'; + + +export default class Blur extends WebGL { + + constructor() { + super('Blur', { + amount: { + type: 'Number', + default: 0, + }, + }) + } + + _passes() { + const passes = Array(this.in.amount.value) + .fill(null) + .map((x, i) => (gl, program) => this._setParams(gl, program)); + return passes; + } + + get frag() { + return ` + precision mediump float; + + // our texture + uniform sampler2D u_image; + + uniform vec2 u_resolution; + uniform vec2 u_textureSize; + + // the texCoords passed in from the vertex shader. + varying vec2 v_texCoord; + + uniform float kernel[9]; + + void main() { + vec2 onePixel = vec2(1.0, 1.0) / u_textureSize; + + float weight = kernel[0] + + kernel[1] + + kernel[2] + + kernel[3] + + kernel[4] + + kernel[5] + + kernel[6] + + kernel[7] + + kernel[8]; + + vec4 colorSum = + texture2D(u_image, v_texCoord + onePixel * vec2(-1, -1)) * kernel[0] + + texture2D(u_image, v_texCoord + onePixel * vec2(0, -1)) * kernel[1] + + texture2D(u_image, v_texCoord + onePixel * vec2(1, -1)) * kernel[2] + + texture2D(u_image, v_texCoord + onePixel * vec2(-1, 0)) * kernel[3] + + texture2D(u_image, v_texCoord + onePixel * vec2(0, 0)) * kernel[4] + + texture2D(u_image, v_texCoord + onePixel * vec2(1, 0)) * kernel[5] + + texture2D(u_image, v_texCoord + onePixel * vec2(-1, 1)) * kernel[6] + + texture2D(u_image, v_texCoord + onePixel * vec2(0, 1)) * kernel[7] + + texture2D(u_image, v_texCoord + onePixel * vec2(1, 1)) * kernel[8]; + + gl_FragColor = colorSum / weight; + } + ` + } + + get amount() { + return this.in.amount.value; + } + + /** + * + * @param {WebGLRenderingContext} gl + * @param {*} program + */ + _setParams(gl, program) { + gl.uniform1fv(gl.getUniformLocation(program, 'kernel'), [ + 0.045, 0.122, 0.045, + 0.122, 0.332, 0.122, + 0.045, 0.122, 0.045 + ]); + } +} \ No newline at end of file diff --git a/src/nodes/WebGL/WebGl.js b/src/nodes/WebGL/WebGl.js index a6b0488..aa983fd 100644 --- a/src/nodes/WebGL/WebGl.js +++ b/src/nodes/WebGL/WebGl.js @@ -30,10 +30,13 @@ export default class WebGL extends Node { get vert() { return ` + precision mediump float; attribute vec2 a_position; attribute vec2 a_texCoord; uniform vec2 u_resolution; + uniform vec2 u_textureSize; + uniform float u_flip_y; varying vec2 v_texCoord; @@ -47,7 +50,7 @@ export default class WebGL extends Node { // convert from 0->2 to -1->+1 (clipspace) vec2 clipSpace = zeroToTwo - 1.0; - gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); + gl_Position = vec4(clipSpace * vec2(1, u_flip_y), 0, 1); // pass the texCoord to the fragment shader // The GPU will interpolate this value between points. @@ -133,6 +136,37 @@ export default class WebGL extends Node { return texture; } + _createFrameBuffer(gl, {width, height}) { + const texture = this.createTexture(gl); + + const frameBuffer = gl.createFramebuffer(); + gl.texImage2D( + gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, + gl.RGBA, gl.UNSIGNED_BYTE, null); + + gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + return {frameBuffer, texture}; + } + + _passes() { + return [ + (gl, program, image) => { + console.log('pass1') + this._setParams(gl, program); + }, + (gl, program, image) => { + console.log('pass2') + this._setParams(gl, program); + }, + (gl, program, image) => { + console.log('pass3') + this._setParams(gl, program); + } + ] + } + _setParams(gl, program) { } @@ -146,7 +180,6 @@ export default class WebGL extends Node { if (!this.program) { this.program = this.compileProgram(gl); gl.useProgram(this.program); - this.createTexture(gl); } } @@ -161,6 +194,7 @@ export default class WebGL extends Node { if (width === 0 || height === 0) { return; } + this.setup(); const canvas = this.canvas; canvas.width = width; @@ -169,6 +203,14 @@ export default class WebGL extends Node { const gl = canvas.getContext('webgl'); const program = this.program; + const frameBuffers = [ + this._createFrameBuffer(gl, {width, height}), + this._createFrameBuffer(gl, {width, height}), + ]; + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + this.createTexture(gl); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); @@ -177,13 +219,13 @@ export default class WebGL extends Node { const positionLocation = gl.getAttribLocation(program, "a_position"); const texcoordLocation = gl.getAttribLocation(program, "a_texCoord"); const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); + const flipLocation = gl.getUniformLocation(program, "u_flip_y"); if (this.image) { // Upload the image into the texture. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, await createImageBitmap(this.image)); } - const texcoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ @@ -226,13 +268,27 @@ export default class WebGL extends Node { gl.vertexAttribPointer( texcoordLocation, size, type, normalize, stride, offset); - // set the resolution - gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); + const passes = this._passes(); + for (let iter = 0; iter < passes.length; iter++) { + const frameBuffer = frameBuffers[iter%2]; + if (iter === passes.length -1) { // last + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.uniform1f(flipLocation, -1); + } else { + gl.uniform1f(flipLocation, 1); + gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer.frameBuffer); + } - this._setParams(gl, program); + passes[iter](gl, program); + // set the resolution + gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); + gl.uniform2f(gl.getUniformLocation(program, "u_textureSize"), width, height); - // Draw the rectangle. - gl.drawArrays(gl.TRIANGLES, 0, 6); + // Draw the rectangle. + gl.drawArrays(gl.TRIANGLES, 0, 6); + + gl.bindTexture(gl.TEXTURE_2D, frameBuffer.texture); + } const resultCanvas = canvasPool2D.createCanvas(); resultCanvas.width = 1; diff --git a/src/nodes/index.js b/src/nodes/index.js index f6791b4..16dc048 100644 --- a/src/nodes/index.js +++ b/src/nodes/index.js @@ -11,7 +11,8 @@ export {default as BrightnessContrast} from './WebGL/BrightnessContrast'; export {default as HSV} from './WebGL/HSV'; export {default as Sepia} from './WebGL/Sepia'; export {default as GreenScreen} from './WebGL/Greenscreen'; -export {default as Channels} from './WebGL/Channels' +export {default as Channels} from './WebGL/Channels'; +export {default as Blur} from './WebGL/Blur'; // Math export {default as Numbers} from './Math/Numbers'; export {default as NumberBinaryOperation} from './Math/NumberBinaryOperation';