diff --git a/filters/tilt-shift/src/TiltShiftAxisFilter.ts b/filters/tilt-shift/src/TiltShiftAxisFilter.ts index 783db453e..4dc403424 100644 --- a/filters/tilt-shift/src/TiltShiftAxisFilter.ts +++ b/filters/tilt-shift/src/TiltShiftAxisFilter.ts @@ -1,6 +1,7 @@ -import { vertex } from '@tools/fragments'; +import { vertex, wgslVertex } from '@tools/fragments'; import fragment from './tilt-shift.frag'; -import { Filter, GlProgram, Point } from 'pixi.js'; +import source from './tilt-shift.wgsl'; +import { Filter, GlProgram, GpuProgram, PointData } from 'pixi.js'; // @author Vico @vicocotea // original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js @@ -9,16 +10,18 @@ import { Filter, GlProgram, Point } from 'pixi.js'; /** * Options for creating filter. */ -export interface TiltShiftFilterOptions +interface TiltShiftAxisFilterOptions { /** The strength of the blur. */ - blur: number; + blur?: number; /** The strength of the blur gradient */ - gradientBlur: number; + gradientBlur?: number; /** The position to start the effect at. */ - start?: Point | undefined; + start?: PointData; /** The position to end the effect at. */ - end?: Point | undefined; + end?: PointData; + /** The axis that the filter is calculating for. */ + axis?: 'vertical' | 'horizontal'; } /** @@ -30,8 +33,43 @@ export interface TiltShiftFilterOptions */ export class TiltShiftAxisFilter extends Filter { - constructor(options: TiltShiftFilterOptions) + /** Default values for options. */ + public static readonly DEFAULT_OPTIONS: TiltShiftAxisFilterOptions = { + /** The strength of the blur. */ + blur: 100, + /** The strength of the blur gradient */ + gradientBlur: 600, + /** The position to start the effect at. */ + start: { x: 0, y: window.innerHeight / 2 }, + /** The position to end the effect at. */ + end: { x: 600, y: window.innerHeight / 2 }, + }; + + public uniforms: { + uBlur: Float32Array; + uStart: PointData + uEnd: PointData; + uDelta: Float32Array; + uTexSize: Float32Array; + }; + + private _tiltAxis: TiltShiftAxisFilterOptions['axis']; + + constructor(options?: TiltShiftAxisFilterOptions) { + options = { ...TiltShiftAxisFilter.DEFAULT_OPTIONS, ...options } as TiltShiftAxisFilterOptions; + + const gpuProgram = new GpuProgram({ + vertex: { + source: wgslVertex, + entryPoint: 'mainVertex', + }, + fragment: { + source, + entryPoint: 'mainFragment', + }, + }); + const glProgram = new GlProgram({ vertex, fragment, @@ -39,87 +77,101 @@ export class TiltShiftAxisFilter extends Filter }); super({ + gpuProgram, glProgram, - resources: {}, + resources: { + tiltShiftUniforms: { + uBlur: { value: new Float32Array([ + options.blur ?? 100, + options.gradientBlur ?? 600 + ]), type: 'vec2' }, + uStart: { value: options.start, type: 'vec2' }, + uEnd: { value: options.end, type: 'vec2' }, + uDelta: { value: new Float32Array([30, 30]), type: 'vec2' }, + uTexSize: { value: new Float32Array([window.innerWidth, window.innerHeight]), type: 'vec2' }, + }, + }, }); - // this.uniforms.blur = options.blur; - // this.uniforms.gradientBlur = options.gradientBlur; - // this.uniforms.start = options.start ?? new Point(0, window.innerHeight / 2); - // this.uniforms.end = options.end ?? new Point(600, window.innerHeight / 2); - // this.uniforms.delta = new Point(30, 30); - // this.uniforms.texSize = new Point(window.innerWidth, window.innerHeight); + this.uniforms = this.resources.tiltShiftUniforms.uniforms; + this._tiltAxis = options.axis; this.updateDelta(); } - /** - * Updates the filter delta values. - * This is overridden in the X and Y filters, does nothing for this class. - * - */ + /** Updates the filter delta values. */ protected updateDelta(): void { - // this.uniforms.delta.x = 0; - // this.uniforms.delta.y = 0; + this.uniforms.uDelta[0] = 0; + this.uniforms.uDelta[1] = 0; + + if (this._tiltAxis === undefined) return; + + const end = this.uniforms.uEnd; + const start = this.uniforms.uStart; + + const dx = end.x - start.x; + const dy = end.y - start.y; + const d = Math.sqrt((dx * dx) + (dy * dy)); + + const isVert = this._tiltAxis === 'vertical'; + + this.uniforms.uDelta[0] = !isVert ? dx / d : -dy / d; + this.uniforms.uDelta[1] = !isVert ? dy / d : dx / d; } - /** - * The strength of the blur. - * - * @memberof TiltShiftAxisFilter# - */ - // get blur(): number - // { - // return this.uniforms.blur; - // } - // set blur(value: number) - // { - // this.uniforms.blur = value; - // } + // /** The strength of the blur. */ + // get blur(): number { return this.uniforms.uBlur[0]; } + // set blur(value: number) { this.uniforms.uBlur[0] = value; } - /** - * The strength of the gradient blur. - * - * @memberof TiltShiftAxisFilter# - */ - // get gradientBlur(): number + // /** The strength of the gradient blur. */ + // get gradientBlur(): number { return this.uniforms.uBlur[1]; } + // set gradientBlur(value: number) { this.uniforms.uBlur[1] = value; } + + // /** The start position of the effect. */ + // get start(): PointData { return this.uniforms.uStart; } + // set start(value: PointData) // { - // return this.uniforms.gradientBlur; + // this.uniforms.uStart = value; + // this.updateDelta(); // } - // set gradientBlur(value: number) + + // /** The start position of the effect on the `x` axis. */ + // get startX(): number { return this.start.x; } + // set startX(value: number) // { - // this.uniforms.gradientBlur = value; + // this.start.x = value; + // this.updateDelta(); // } - /** - * The X value to start the effect at. - * - * @member {Point} - * @memberof TiltShiftAxisFilter# - */ - // get start(): Point + // /** The start position of the effect on the `y` axis. */ + // get startY(): number { return this.startY; } + // set startY(value: number) // { - // return this.uniforms.start; + // this.start.y = value; + // this.updateDelta(); // } - // set start(value: Point) + + // /** The end position of the effect. */ + // get end(): PointData { return this.uniforms.uEnd; } + // set end(value: PointData) // { - // this.uniforms.start = value; + // this.uniforms.uEnd = value; // this.updateDelta(); // } - /** - * The X value to end the effect at. - * - * @member {Point} - * @memberof TiltShiftAxisFilter# - */ - // get end(): Point + // /** The end position of the effect on the `x` axis. */ + // get endX(): number { return this.end.x; } + // set endX(value: number) // { - // return this.uniforms.end; + // this.end.x = value; + // this.updateDelta(); // } - // set end(value: Point) + + // /** The end position of the effect on the `y` axis. */ + // get endY(): number { return this.end.y; } + // set endY(value: number) // { - // this.uniforms.end = value; + // this.end.y = value; // this.updateDelta(); // } } diff --git a/filters/tilt-shift/src/TiltShiftFilter.ts b/filters/tilt-shift/src/TiltShiftFilter.ts index d2ffbd3ee..b73e9c9c5 100644 --- a/filters/tilt-shift/src/TiltShiftFilter.ts +++ b/filters/tilt-shift/src/TiltShiftFilter.ts @@ -1,13 +1,23 @@ -import { TiltShiftXFilter } from './TiltShiftXFilter'; -import { TiltShiftYFilter } from './TiltShiftYFilter'; -import { Filter, FilterSystem, RenderTexture } from 'pixi.js'; -import type { Point, RenderSurface, Texture } from 'pixi.js'; -import type { TiltShiftFilterOptions } from './TiltShiftAxisFilter'; +import { FilterSystem, TexturePool } from 'pixi.js'; +import type { PointData, RenderSurface, Texture } from 'pixi.js'; +import { TiltShiftAxisFilter } from './TiltShiftAxisFilter'; // @author Vico @vicocotea // original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js // by Evan Wallace : http://madebyevan.com/ +export interface TiltShiftFilterOptions +{ + /** The strength of the blur. */ + blur?: number; + /** The strength of the blur gradient */ + gradientBlur?: number; + /** The position to start the effect at. */ + start?: PointData; + /** The position to end the effect at. */ + end?: PointData; +} + /** * A TiltShift Filter. Manages the pass of both a TiltShiftXFilter and TiltShiftYFilter.
* ![original](../tools/screenshots/dist/original.png)![filter](../tools/screenshots/dist/tilt-shift.png) @@ -17,109 +27,70 @@ import type { TiltShiftFilterOptions } from './TiltShiftAxisFilter'; * @see {@link https://www.npmjs.com/package/@pixi/filter-tilt-shift|@pixi/filter-tilt-shift} * @see {@link https://www.npmjs.com/package/pixi-filters|pixi-filters} */ -export class TiltShiftFilter extends Filter +export class TiltShiftFilter extends TiltShiftAxisFilter { - /** Default options */ - static readonly defaults: TiltShiftFilterOptions = { - /** The strength of the blur. */ - blur: 100, - /** The strength of the blur gradient */ - gradientBlur: 600, - /** The position to start the effect at. */ - start: undefined, - /** The position to end the effect at. */ - end: undefined, - }; - - private tiltShiftXFilter: TiltShiftXFilter; - private tiltShiftYFilter: TiltShiftYFilter; - - /** - * @param {TiltShiftFilterOptions} [options] - The optional parameters of the tilt shift filter. - */ - constructor(options?: Partial); - - /** - * @deprecated since 5.3.0 - * @param {number} [blur=100] - The strength of the blur. - * @param {number} [gradientBlur=600] - The strength of the gradient blur. - * @param {Point} [start=null] - The position to start the effect at. - * @param {Point} [end=null] - The position to end the effect at. - */ - constructor(blur?: number, gradientBlur?: number, start?: Point, end?: Point); + private _tiltShiftYFilter: TiltShiftAxisFilter; - /** @ignore */ - constructor(options?: Partial | number, gradientBlur?: number, start?: Point, end?: Point) + constructor(options?: TiltShiftFilterOptions) { - super(); + options = { ...TiltShiftAxisFilter.DEFAULT_OPTIONS, ...options }; - if (typeof options === 'number') - { - utils.deprecation('5.3.0', 'TiltShiftFilter constructor arguments is deprecated, use options.'); - options = { blur: options, gradientBlur, start, end }; - } + super({ ...options, axis: 'horizontal' }); + this._tiltShiftYFilter = new TiltShiftAxisFilter({ ...options, axis: 'vertical' }); - options = Object.assign({}, TiltShiftFilter.defaults, options); - - this.tiltShiftXFilter = new TiltShiftXFilter(options as TiltShiftFilterOptions); - this.tiltShiftYFilter = new TiltShiftYFilter(options as TiltShiftFilterOptions); + Object.assign(this, options); } - apply(filterManager: FilterSystem, input: Texture, output: RenderSurface, clear: boolean): void + /** + * Override existing apply method in `Filter` + * @override + * @ignore + */ + public override apply( + filterManager: FilterSystem, + input: Texture, + output: RenderSurface, + clearMode: boolean, + ): void { - const renderTarget = filterManager.getFilterTexture(); + const renderTarget = TexturePool.getSameSizeTexture(input); - this.tiltShiftXFilter.apply(filterManager, input, renderTarget, 1); - this.tiltShiftYFilter.apply(filterManager, renderTarget, output, clearMode); - filterManager.returnFilterTexture(renderTarget); + filterManager.applyFilter(this, input, renderTarget, true); + filterManager.applyFilter(this._tiltShiftYFilter, renderTarget, output, clearMode); + + TexturePool.returnTexture(renderTarget); } /** The strength of the blur. */ - get blur(): number - { - return this.tiltShiftXFilter.blur; - } - set blur(value: number) - { - this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value; - } + get blur(): number { return this.uniforms.uBlur[0]; } + set blur(value: number) { this.uniforms.uBlur[0] = this._tiltShiftYFilter.uniforms.uBlur[0] = value; } /** The strength of the gradient blur. */ - get gradientBlur(): number - { - return this.tiltShiftXFilter.gradientBlur; - } - set gradientBlur(value: number) - { - this.tiltShiftXFilter.gradientBlur = this.tiltShiftYFilter.gradientBlur = value; - } + get gradientBlur(): number { return this.uniforms.uBlur[1]; } + set gradientBlur(value: number) { this.uniforms.uBlur[1] = this._tiltShiftYFilter.uniforms.uBlur[1] = value; } - /** - * The position to start the effect at. - * - * @member {Point} - */ - get start(): Point - { - return this.tiltShiftXFilter.start; - } - set start(value: Point) - { - this.tiltShiftXFilter.start = this.tiltShiftYFilter.start = value; - } + /** The position to start the effect at. */ + get start(): PointData { return this.uniforms.uStart; } + set start(value: PointData) { this.uniforms.uStart = this._tiltShiftYFilter.uniforms.uStart = value; } - /** - * The position to end the effect at. - * - * @member {Point} - */ - get end(): Point - { - return this.tiltShiftXFilter.end; - } - set end(value: Point) - { - this.tiltShiftXFilter.end = this.tiltShiftYFilter.end = value; - } + /** The position to start the effect at on the `x` axis. */ + get startX(): number { return this.start.x; } + set startX(value: number) { this.start.x = value; } + + /** The position to start the effect at on the `x` axis. */ + get startY(): number { return this.start.y; } + set startY(value: number) { this.start.y = value; } + + /** The position to end the effect at. */ + get end(): PointData { return this.uniforms.uEnd; } + set end(value: PointData) { this.uniforms.uEnd = this._tiltShiftYFilter.uniforms.uEnd = value; } + + /** The position to end the effect at on the `x` axis. */ + get endX(): number { return this.end.x; } + set endX(value: number) { this.end.x = value; } + + /** The position to end the effect at on the `y` axis. */ + get endY(): number { return this.end.y; } + set endY(value: number) { this.end.y = value; } } diff --git a/filters/tilt-shift/src/TiltShiftXFilter.ts b/filters/tilt-shift/src/TiltShiftXFilter.ts deleted file mode 100644 index 9dfef7ddf..000000000 --- a/filters/tilt-shift/src/TiltShiftXFilter.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TiltShiftAxisFilter } from './TiltShiftAxisFilter'; - -// @author Vico @vicocotea -// original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js -// by Evan Wallace : http://madebyevan.com/ - -/** - * A TiltShiftXFilter. - * - * @class - * @extends TiltShiftAxisFilter - * @private - */ -export class TiltShiftXFilter extends TiltShiftAxisFilter -{ - /** - * Updates the filter delta values. - */ - protected updateDelta(): void - { - // const dx = this.uniforms.end.x - this.uniforms.start.x; - // const dy = this.uniforms.end.y - this.uniforms.start.y; - // const d = Math.sqrt((dx * dx) + (dy * dy)); - - // this.uniforms.delta.x = dx / d; - // this.uniforms.delta.y = dy / d; - } -} diff --git a/filters/tilt-shift/src/TiltShiftYFilter.ts b/filters/tilt-shift/src/TiltShiftYFilter.ts deleted file mode 100644 index 66cfe0071..000000000 --- a/filters/tilt-shift/src/TiltShiftYFilter.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TiltShiftAxisFilter } from './TiltShiftAxisFilter'; - -// @author Vico @vicocotea -// original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js -// by Evan Wallace : http://madebyevan.com/ - -/** - * A TiltShiftYFilter. - * - * @class - * @extends TiltShiftAxisFilter - * @private - */ -export class TiltShiftYFilter extends TiltShiftAxisFilter -{ - /** - * Updates the filter delta values. - */ - protected updateDelta(): void - { - // const dx = this.uniforms.end.x - this.uniforms.start.x; - // const dy = this.uniforms.end.y - this.uniforms.start.y; - // const d = Math.sqrt((dx * dx) + (dy * dy)); - - // this.uniforms.delta.x = -dy / d; - // this.uniforms.delta.y = dx / d; - } -} diff --git a/filters/tilt-shift/src/index.ts b/filters/tilt-shift/src/index.ts index a72662387..7b86d525b 100644 --- a/filters/tilt-shift/src/index.ts +++ b/filters/tilt-shift/src/index.ts @@ -1,4 +1,2 @@ export * from './TiltShiftFilter'; -export * from './TiltShiftXFilter'; -export * from './TiltShiftYFilter'; export * from './TiltShiftAxisFilter'; diff --git a/filters/tilt-shift/src/tilt-shift.frag b/filters/tilt-shift/src/tilt-shift.frag index e8894a04f..25a9f9722 100644 --- a/filters/tilt-shift/src/tilt-shift.frag +++ b/filters/tilt-shift/src/tilt-shift.frag @@ -1,12 +1,12 @@ -varying vec2 vTextureCoord; +in vec2 vTextureCoord; +out vec4 finalColor; uniform sampler2D uSampler; -uniform float blur; -uniform float gradientBlur; -uniform vec2 start; -uniform vec2 end; -uniform vec2 delta; -uniform vec2 texSize; +uniform vec2 uBlur; +uniform vec2 uStart; +uniform vec2 uEnd; +uniform vec2 uDelta; +uniform vec2 uTexSize; float random(vec3 scale, float seed) { @@ -18,15 +18,18 @@ void main(void) vec4 color = vec4(0.0); float total = 0.0; + float blur = uBlur[0]; + float gradientBlur = uBlur[1]; + float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); - vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x)); - float radius = smoothstep(0.0, 1.0, abs(dot(vTextureCoord * texSize - start, normal)) / gradientBlur) * blur; + vec2 normal = normalize(vec2(uStart.y - uEnd.y, uEnd.x - uStart.x)); + float radius = smoothstep(0.0, 1.0, abs(dot(vTextureCoord * uTexSize - uStart, normal)) / gradientBlur) * blur; for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); - vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * radius); + vec4 sample = texture(uSampler, vTextureCoord + uDelta / uTexSize * percent * radius); sample.rgb *= sample.a; color += sample * weight; total += weight; @@ -35,5 +38,5 @@ void main(void) color /= total; color.rgb /= color.a + 0.00001; - gl_FragColor = color; + finalColor = color; } diff --git a/filters/tilt-shift/src/tilt-shift.wgsl b/filters/tilt-shift/src/tilt-shift.wgsl new file mode 100644 index 000000000..e0ab2a779 --- /dev/null +++ b/filters/tilt-shift/src/tilt-shift.wgsl @@ -0,0 +1,51 @@ +struct TiltShiftUniforms { + uBlur: vec2, + uStart: vec2, + uEnd: vec2, + uDelta: vec2, + uTexSize: vec2, +}; + +@group(0) @binding(1) var uSampler: texture_2d; +@group(1) @binding(0) var tiltShiftUniforms : TiltShiftUniforms; + +@fragment +fn mainFragment( + @builtin(position) position: vec4, + @location(0) uv : vec2 +) -> @location(0) vec4 { + let uBlur = tiltShiftUniforms.uBlur[0]; + let uBlurGradient = tiltShiftUniforms.uBlur[1]; + let uStart = tiltShiftUniforms.uStart; + let uEnd = tiltShiftUniforms.uEnd; + let uDelta = tiltShiftUniforms.uDelta; + let uTexSize = tiltShiftUniforms.uTexSize; + + var color: vec4 = vec4(0.0); + var total: f32 = 0.0; + + let offset: f32 = random(position, vec3(12.9898, 78.233, 151.7182), 0.0); + let normal: vec2 = normalize(vec2(uStart.y - uEnd.y, uEnd.x - uStart.x)); + let radius: f32 = smoothstep(0.0, 1.0, abs(dot(uv * uTexSize - uStart, normal)) / uBlurGradient) * uBlur; + + for (var t: f32 = -30.0; t <= 30.0; t += 1.0) + { + var percent: f32 = (t + offset - 0.5) / 30.0; + var weight: f32 = 1.0 - abs(percent); + var sample: vec4 = textureSample(uSampler, uSampler, uv + uDelta / uTexSize * percent * radius); + sample = vec4(sample.xyz * sample.a, sample.a); // multiply sample.rgb with sample.a + color += sample * weight; + total += weight; + } + + color /= total; + color = vec4(color.xyz / (color.a + 0.00001), color.a); // divide color.rgb by color.a + 0.00001 + + return color; +} + + +fn random(position: vec4, scale: vec3, seed: f32) -> f32 +{ + return fract(sin(dot(position.xyz + seed, scale)) * 43758.5453 + seed); +} \ No newline at end of file diff --git a/tools/demo/src/index.js b/tools/demo/src/index.js index 33d3192a3..ff675e571 100644 --- a/tools/demo/src/index.js +++ b/tools/demo/src/index.js @@ -65,6 +65,7 @@ const main = async () => filters.glitch.call(app); filters.advancedBloom.call(app); filters.oldFilm.call(app); + filters.tiltShift.call(app); // TODO: Re-enable this in place of the above once v8 conversion is complete // for (const i in filters)