✨ 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() {
|
||||
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,14 +268,28 @@ export default class WebGL extends Node {
|
||||
gl.vertexAttribPointer(
|
||||
texcoordLocation, size, type, normalize, stride, offset);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
passes[iter](gl, program);
|
||||
// set the resolution
|
||||
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
|
||||
|
||||
this._setParams(gl, program);
|
||||
gl.uniform2f(gl.getUniformLocation(program, "u_textureSize"), width, height);
|
||||
|
||||
// Draw the rectangle.
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, frameBuffer.texture);
|
||||
}
|
||||
|
||||
const resultCanvas = canvasPool2D.createCanvas();
|
||||
resultCanvas.width = 1;
|
||||
resultCanvas.height = 1;
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user