✨ 2 pass gaussian blur
This commit is contained in:
@@ -1,82 +0,0 @@
|
|||||||
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
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
147
src/nodes/WebGL/Blur.ts
Normal file
147
src/nodes/WebGL/Blur.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import WebGL from './WebGl';
|
||||||
|
import {
|
||||||
|
ImageVar,
|
||||||
|
NumberVar
|
||||||
|
} from '../io/AbstractIOSet';
|
||||||
|
|
||||||
|
const inputs = {
|
||||||
|
image: {
|
||||||
|
type: 'Image',
|
||||||
|
} as ImageVar,
|
||||||
|
radius: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
} as NumberVar,
|
||||||
|
sigma: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 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 Blur extends WebGL<typeof inputs> {
|
||||||
|
|
||||||
|
_lastRadiusValue?: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('Blur', 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 blur
|
||||||
|
},
|
||||||
|
(gl, program) => {
|
||||||
|
this._setDirection(gl, program, [0, 1]) // Vertical blur
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
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 += (color1 + color2) * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = colorSum / totalWeight;
|
||||||
|
gl_FragColor.a = 1.0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -317,15 +317,21 @@ export default class WebGL<I extends Variables> extends Node<I & (typeof inputs)
|
|||||||
for (let {i, tex, location, name} of textures) {
|
for (let {i, tex, location, name} of textures) {
|
||||||
// console.log('bind', i, name, this.in[name].value, location, [gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2, gl.TEXTURE3][i]);
|
// console.log('bind', i, name, this.in[name].value, location, [gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2, gl.TEXTURE3][i]);
|
||||||
if (this.in[name].value) {
|
if (this.in[name].value) {
|
||||||
|
if (name === 'image' && iter > 0) {
|
||||||
|
gl.activeTexture([gl.TEXTURE2, gl.TEXTURE3][i]);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, frameBuffers[(iter-1)%2].texture);
|
||||||
|
gl.uniform1i(location, i + 2);
|
||||||
|
} else {
|
||||||
gl.uniform1i(location, i + 2);
|
gl.uniform1i(location, i + 2);
|
||||||
gl.activeTexture([gl.TEXTURE2, gl.TEXTURE3][i]);
|
gl.activeTexture([gl.TEXTURE2, gl.TEXTURE3][i]);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
gl.bindTexture(gl.TEXTURE_2D, frameBuffer.texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultCanvas = canvasPool2D.createCanvas();
|
const resultCanvas = canvasPool2D.createCanvas();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export {default as Channels} from './WebGL/Channels';
|
|||||||
export {default as GradientMap} from './WebGL/GradientMap';
|
export {default as GradientMap} from './WebGL/GradientMap';
|
||||||
export {default as ColorLookupTable} from './WebGL/ColorLookupTable';
|
export {default as ColorLookupTable} from './WebGL/ColorLookupTable';
|
||||||
export {default as Vignette} from './WebGL/Vignette';
|
export {default as Vignette} from './WebGL/Vignette';
|
||||||
// export {default as Blur} from './WebGL/Blur';
|
export {default as Blur} from './WebGL/Blur';
|
||||||
export {default as Edge} from './WebGL/Edge';
|
export {default as Edge} from './WebGL/Edge';
|
||||||
// Math
|
// Math
|
||||||
export {default as Numbers} from './Math/Numbers';
|
export {default as Numbers} from './Math/Numbers';
|
||||||
|
|||||||
Reference in New Issue
Block a user