Skip to content

Commit

Permalink
Update Bulge Pinch Filter
Browse files Browse the repository at this point in the history
  • Loading branch information
bbazukun123 committed Jan 3, 2024
1 parent 570a5fc commit b451567
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 96 deletions.
158 changes: 97 additions & 61 deletions filters/bulge-pinch/src/BulgePinchFilter.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { vertex } from '@tools/fragments';
import fragment from './bulgePinch.frag';
import { Filter, GlProgram } from 'pixi.js';
import type { Point, FilterSystem, Texture, RenderSurface } from 'pixi.js';
import { vertex, wgslVertex } from '@tools/fragments';
import fragment from './bulge-pinch.frag';
import source from './bulge-pinch.wgsl';
import { Filter, GlProgram, GpuProgram } from 'pixi.js';
import type { FilterSystem, Texture, RenderSurface, PointData } from 'pixi.js';

type PointLike = Point | number[];
// This WebGPU filter has been ported from the WebGL renderer that was originally created by Julien CLEREL (@JuloxRox)

export interface BulgePinchFilterOptions
{
center: PointLike;
radius: number;
strength: number;
/**
* Offset coordinates to change the position of the center of the circle of effect.
* @default {x:0,y:0}
*/
center?: PointData;
/**
* The radius of the circle of effect
* @default 100
*/
radius?: number;
/**
* A value between -1 and 1 (-1 is strong pinch, 0 is no effect, 1 is strong bulge)
* @default 1
*/
strength?: number;
}

// @author Julien CLEREL @JuloxRox
// original filter https://github.com/evanw/glfx.js/blob/master/src/filters/warp/bulgepinch.js
// by Evan Wallace : http://madebyevan.com/

/**
* Bulges or pinches the image in a circle.<br>
* ![original](../tools/screenshots/dist/original.png)![filter](../tools/screenshots/dist/bulge-pinch.gif)
Expand All @@ -27,82 +36,109 @@ export interface BulgePinchFilterOptions
*/
export class BulgePinchFilter extends Filter
{
/** Default constructor options. */
public static readonly defaults: BulgePinchFilterOptions = {
center: [0.5, 0.5],
/** Default values for options. */
public static readonly DEFAULT_OPTIONS: BulgePinchFilterOptions = {
center: { x: 0.5, y: 0.5 },
radius: 100,
strength: 1,
strength: 1
};

/**
* @param {object} [options] - Options to use for filter.
* @param {Point|Array<number>} [options.center=[0,0]] - The x and y coordinates of the center
* of the circle of effect.
* @param {number} [options.radius=100] - The radius of the circle of effect.
* @param {number} [options.strength=1] - -1 to 1 (-1 is strong pinch, 0 is no effect, 1 is strong bulge)
*/
constructor(options?: Partial<BulgePinchFilterOptions>)
public uniforms: {
uDimensions: Float32Array;
uCenter: PointData;
uRadius: number;
uStrength: number;
};

constructor(options?: BulgePinchFilterOptions)
{
options = { ...BulgePinchFilter.DEFAULT_OPTIONS, ...options };

const gpuProgram = new GpuProgram({
vertex: {
source: wgslVertex,
entryPoint: 'mainVertex',
},
fragment: {
source,
entryPoint: 'mainFragment',
},
});
const glProgram = new GlProgram({
vertex,
fragment,
name: 'bulge-pinch-filter',
});

super({
gpuProgram,
glProgram,
resources: {},
resources: {
bulgePinchUniforms: {
uDimensions: { value: [0, 0], type: 'vec2<f32>' },
uCenter: { value: options.center, type: 'vec2<f32>' },
uRadius: { value: options.radius, type: 'f32' },
uStrength: { value: options.strength, type: 'f32' },
}
},
});

// this.uniforms.dimensions = new Float32Array(2);
this.uniforms = this.resources.bulgePinchUniforms.uniforms;

Object.assign(this, BulgePinchFilter.defaults, options);
Object.assign(this, options);
}

apply(filterManager: FilterSystem, input: Texture, output: RenderSurface, clear: boolean): void
/**
* Override existing apply method in `Filter`
* @override
* @ignore
*/
public apply(
filterManager: FilterSystem,
input: Texture,
output: RenderSurface,
clearMode: boolean
): void
{
// const { width, height } = input.filterFrame as Rectangle;
this.uniforms.uDimensions[0] = input.frame.width;
this.uniforms.uDimensions[1] = input.frame.height;

// this.uniforms.dimensions[0] = width;
// this.uniforms.dimensions[1] = height;
// filterManager.applyFilter(this, input, output, clear);
filterManager.applyFilter(this, input, output, clearMode);
}

/**
* The radius of the circle of effect.
* Sets the center of the effect in normalized screen coords.
* { x: 0, y: 0 } means top-left and { x: 1, y: 1 } mean bottom-right
* @default {x:0.5,y:0.5}
*/
get center(): PointData { return this.uniforms.uCenter; }
set center(value: PointData) { this.uniforms.uCenter = value; }

/**
* Sets the center of the effect in normalized screen coords on the `x` axis
* @default 0
*/
get centerX(): number { return this.uniforms.uCenter.x; }
set centerX(value: number) { this.uniforms.uCenter.x = value; }

/**
* Sets the center of the effect in normalized screen coords on the `y` axis
* @default 0
*/
// get radius(): number
// {
// return this.uniforms.radius;
// }
// set radius(value: number)
// {
// this.uniforms.radius = value;
// }
get centerY(): number { return this.uniforms.uCenter.y; }
set centerY(value: number) { this.uniforms.uCenter.y = value; }

/**
* The strength of the effect. -1 to 1 (-1 is strong pinch, 0 is no effect, 1 is strong bulge)
* The radius of the circle of effect
* @default 100
*/
// get strength(): number
// {
// return this.uniforms.strength;
// }
// set strength(value: number)
// {
// this.uniforms.strength = value;
// }
get radius(): number { return this.uniforms.uRadius; }
set radius(value: number) { this.uniforms.uRadius = value; }

/**
* The x and y coordinates of the center of the circle of effect.
*
* @member {Point | Array<number>}
* A value between -1 and 1 (-1 is strong pinch, 0 is no effect, 1 is strong bulge)
* @default 1
*/
// get center(): PointLike
// {
// return this.uniforms.center;
// }
// set center(value: PointLike)
// {
// this.uniforms.center = value;
// }
get strength(): number { return this.uniforms.uStrength; }
set strength(value: number) { this.uniforms.uStrength = value; }
}
39 changes: 39 additions & 0 deletions filters/bulge-pinch/src/bulge-pinch.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
precision highp float;
in vec2 vTextureCoord;
out vec4 finalColor;

uniform sampler2D uSampler;
uniform vec2 uDimensions;
uniform vec2 uCenter;
uniform float uRadius;
uniform float uStrength;

uniform vec4 uInputSize;
uniform vec4 uInputClamp;

void main()
{
vec2 coord = vTextureCoord * uInputSize.xy;
coord -= uCenter * uDimensions.xy;
float distance = length(coord);

if (distance < uRadius) {
float percent = distance / uRadius;
if (uStrength > 0.0) {
coord *= mix(1.0, smoothstep(0.0, uRadius / distance, percent), uStrength * 0.75);
} else {
coord *= mix(1.0, pow(percent, 1.0 + uStrength * 0.75) * uRadius / distance, 1.0 - percent);
}
}

coord += uCenter * uDimensions.xy;
coord /= uInputSize.xy;
vec2 clampedCoord = clamp(coord, uInputClamp.xy, uInputClamp.zw);
vec4 color = texture(uSampler, clampedCoord);

if (coord != clampedCoord) {
color *= max(0.0, 1.0 - length(coord - clampedCoord));
}

finalColor = color;
}
63 changes: 63 additions & 0 deletions filters/bulge-pinch/src/bulge-pinch.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
struct BulgePinchUniforms {
uDimensions: vec2<f32>,
uCenter: vec2<f32>,
uRadius: f32,
uStrength: f32,
};

struct GlobalFilterUniforms {
uInputSize:vec4<f32>,
uInputPixel:vec4<f32>,
uInputClamp:vec4<f32>,
uOutputFrame:vec4<f32>,
uGlobalFrame:vec4<f32>,
uOutputTexture:vec4<f32>,
};

@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;

@group(0) @binding(1) var uSampler: texture_2d<f32>;
@group(1) @binding(0) var<uniform> bulgePinchUniforms : BulgePinchUniforms;

@fragment
fn mainFragment(
@builtin(position) position: vec4<f32>,
@location(0) uv : vec2<f32>
) -> @location(0) vec4<f32> {
let dimensions: vec2<f32> = bulgePinchUniforms.uDimensions;
let center: vec2<f32> = bulgePinchUniforms.uCenter;
let radius: f32 = bulgePinchUniforms.uRadius;
let strength: f32 = bulgePinchUniforms.uStrength;
var coord: vec2<f32> = (uv * gfu.uInputSize.xy) - center * dimensions.xy;

let distance: f32 = length(coord);

if (distance < radius) {
let percent: f32 = distance / radius;
if (strength > 0.0) {
coord *= mix(1.0, smoothstep(0.0, radius / distance, percent), strength * 0.75);
} else {
coord *= mix(1.0, pow(percent, 1.0 + strength * 0.75) * radius / distance, 1.0 - percent);
}
}
coord += (center * dimensions.xy);
coord /= gfu.uInputSize.xy;

let clampedCoord: vec2<f32> = clamp(coord, gfu.uInputClamp.xy, gfu.uInputClamp.zw);
var color: vec4<f32> = textureSample(uSampler, uSampler, clampedCoord);
if (coord.x != clampedCoord.x && coord.y != clampedCoord.y) {
color *= max(0.0, 1.0 - length(coord - clampedCoord));
}

return color;
}

fn compareVec2(x: vec2<f32>, y: vec2<f32>) -> bool
{
if (x.x == y.x && x.y == y.y)
{
return true;
}

return false;
}
33 changes: 0 additions & 33 deletions filters/bulge-pinch/src/bulgePinch.frag

This file was deleted.

4 changes: 2 additions & 2 deletions tools/demo/src/filters/bulge-pinch.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default function ()
{
folder.add(this, 'radius', 0, 1000);
folder.add(this, 'strength', -1, 1);
folder.add(this.center, '0', 0, 1).name('center.x');
folder.add(this.center, '1', 0, 1).name('center.y');
folder.add(this, 'centerX', 0, 1).name('center.x');
folder.add(this, 'centerY', 0, 1).name('center.y');
});
}
1 change: 1 addition & 0 deletions tools/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const main = async () =>
filters.multiColorReplace.call(app);
filters.colorMap.call(app);
filters.colorGradient.call(app);
filters.bulgePinch.call(app);
// filters.kawaseBlur.call(app);

// TODO: Re-enable this in place of the above once v8 conversion is complete
Expand Down

0 comments on commit b451567

Please sign in to comment.