✨ softedge
This commit is contained in:
152
src/nodes/WebGL/SoftEdge.ts
Normal file
152
src/nodes/WebGL/SoftEdge.ts
Normal file
@@ -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<typeof inputs> {
|
||||
|
||||
_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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user