✨ multipass filter
This commit is contained in:
82
src/nodes/WebGL/Blur.js
Normal file
82
src/nodes/WebGL/Blur.js
Normal file
@@ -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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,10 +30,13 @@ export default class WebGL extends Node {
|
|||||||
|
|
||||||
get vert() {
|
get vert() {
|
||||||
return `
|
return `
|
||||||
|
precision mediump float;
|
||||||
attribute vec2 a_position;
|
attribute vec2 a_position;
|
||||||
attribute vec2 a_texCoord;
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
uniform vec2 u_resolution;
|
uniform vec2 u_resolution;
|
||||||
|
uniform vec2 u_textureSize;
|
||||||
|
uniform float u_flip_y;
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
@@ -47,7 +50,7 @@ export default class WebGL extends Node {
|
|||||||
// convert from 0->2 to -1->+1 (clipspace)
|
// convert from 0->2 to -1->+1 (clipspace)
|
||||||
vec2 clipSpace = zeroToTwo - 1.0;
|
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
|
// pass the texCoord to the fragment shader
|
||||||
// The GPU will interpolate this value between points.
|
// The GPU will interpolate this value between points.
|
||||||
@@ -133,6 +136,37 @@ export default class WebGL extends Node {
|
|||||||
return texture;
|
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) {
|
_setParams(gl, program) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -146,7 +180,6 @@ export default class WebGL extends Node {
|
|||||||
if (!this.program) {
|
if (!this.program) {
|
||||||
this.program = this.compileProgram(gl);
|
this.program = this.compileProgram(gl);
|
||||||
gl.useProgram(this.program);
|
gl.useProgram(this.program);
|
||||||
this.createTexture(gl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +194,7 @@ export default class WebGL extends Node {
|
|||||||
if (width === 0 || height === 0) {
|
if (width === 0 || height === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setup();
|
this.setup();
|
||||||
const canvas = this.canvas;
|
const canvas = this.canvas;
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
@@ -169,6 +203,14 @@ export default class WebGL extends Node {
|
|||||||
const gl = canvas.getContext('webgl');
|
const gl = canvas.getContext('webgl');
|
||||||
const program = this.program;
|
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.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||||
gl.clearColor(0, 0, 0, 0);
|
gl.clearColor(0, 0, 0, 0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
@@ -177,13 +219,13 @@ export default class WebGL extends Node {
|
|||||||
const positionLocation = gl.getAttribLocation(program, "a_position");
|
const positionLocation = gl.getAttribLocation(program, "a_position");
|
||||||
const texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
|
const texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
|
||||||
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
|
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
|
||||||
|
const flipLocation = gl.getUniformLocation(program, "u_flip_y");
|
||||||
|
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
// Upload the image into the texture.
|
// Upload the image into the texture.
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, await createImageBitmap(this.image));
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, await createImageBitmap(this.image));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const texcoordBuffer = gl.createBuffer();
|
const texcoordBuffer = gl.createBuffer();
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||||
@@ -226,13 +268,27 @@ export default class WebGL extends Node {
|
|||||||
gl.vertexAttribPointer(
|
gl.vertexAttribPointer(
|
||||||
texcoordLocation, size, type, normalize, stride, offset);
|
texcoordLocation, size, type, normalize, stride, offset);
|
||||||
|
|
||||||
// set the resolution
|
const passes = this._passes();
|
||||||
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
|
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.
|
// Draw the rectangle.
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, frameBuffer.texture);
|
||||||
|
}
|
||||||
|
|
||||||
const resultCanvas = canvasPool2D.createCanvas();
|
const resultCanvas = canvasPool2D.createCanvas();
|
||||||
resultCanvas.width = 1;
|
resultCanvas.width = 1;
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export {default as BrightnessContrast} from './WebGL/BrightnessContrast';
|
|||||||
export {default as HSV} from './WebGL/HSV';
|
export {default as HSV} from './WebGL/HSV';
|
||||||
export {default as Sepia} from './WebGL/Sepia';
|
export {default as Sepia} from './WebGL/Sepia';
|
||||||
export {default as GreenScreen} from './WebGL/Greenscreen';
|
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
|
// Math
|
||||||
export {default as Numbers} from './Math/Numbers';
|
export {default as Numbers} from './Math/Numbers';
|
||||||
export {default as NumberBinaryOperation} from './Math/NumberBinaryOperation';
|
export {default as NumberBinaryOperation} from './Math/NumberBinaryOperation';
|
||||||
|
|||||||
Reference in New Issue
Block a user