diff --git a/filters/pixelate/src/PixelateFilter.ts b/filters/pixelate/src/PixelateFilter.ts index 806ffd6e5..6a41e1056 100644 --- a/filters/pixelate/src/PixelateFilter.ts +++ b/filters/pixelate/src/PixelateFilter.ts @@ -1,7 +1,7 @@ -import { vertex } from '@tools/fragments'; +import { vertex, wgslVertex } from '@tools/fragments'; import fragment from './pixelate.frag'; -import { Filter, GlProgram } from 'pixi.js'; -import type { Point } from 'pixi.js'; +import source from './pixelate.wgsl'; +import { Filter, GlProgram, GpuProgram, UniformGroup, Point } from 'pixi.js'; type Size = number | number[] | Point; @@ -16,11 +16,31 @@ type Size = number | number[] | Point; */ export class PixelateFilter extends Filter { + /** Default values for options. */ + public static readonly DEFAULT_SIZE: Size = 10; + /** * @param {Point|Array|number} [size=10] - Either the width/height of the size of the pixels, or square size */ - constructor(size: Size = 10) + constructor(size: Size) { + size = size ?? PixelateFilter.DEFAULT_SIZE; + + const pixelateUniforms = new UniformGroup({ + uSize: { value: new Float32Array(2), type: 'vec2' }, + }); + + const gpuProgram = new GpuProgram({ + vertex: { + source: wgslVertex, + entryPoint: 'mainVertex', + }, + fragment: { + source, + entryPoint: 'mainFragment', + }, + }); + const glProgram = new GlProgram({ vertex, fragment, @@ -28,30 +48,49 @@ export class PixelateFilter extends Filter }); super({ + gpuProgram, glProgram, - resources: {}, + resources: { + pixelateUniforms, + }, }); - // this.size = size; + this.size = size; } /** - * This a point that describes the size of the blocks. - * x is the width of the block and y is the height. - * - * @member {Point|Array|number} - * @default 10 + * The size of the pixels + * @default [10,10] */ - // get size(): Size - // { - // return this.uniforms.size; - // } - // set size(value: Size) - // { - // if (typeof value === 'number') - // { - // value = [value, value]; - // } - // this.uniforms.size = value; - // } + get size(): Size { return this.resources.pixelateUniforms.uniforms.uSize; } + set size(value: Size) + { + if (value instanceof Point) + { + this.sizeX = value.x; + this.sizeY = value.y; + } + else if (Array.isArray(value)) + { + this.resources.pixelateUniforms.uniforms.uSize = value; + } + else + { + this.sizeX = this.sizeY = value; + } + } + + /** + * The size of the pixels on the `x` axis + * @default 10 + */ + get sizeX(): number { return this.resources.pixelateUniforms.uniforms.uSize[0]; } + set sizeX(value: number) { this.resources.pixelateUniforms.uniforms.uSize[0] = value; } + + /** + * The size of the pixels on the `y` axis + * @default 10 + */ + get sizeY(): number { return this.resources.pixelateUniforms.uniforms.uSize[1]; } + set sizeY(value: number) { this.resources.pixelateUniforms.uniforms.uSize[1] = value; } } diff --git a/filters/pixelate/src/pixelate.frag b/filters/pixelate/src/pixelate.frag index 80ad2ee09..02e7bc69a 100644 --- a/filters/pixelate/src/pixelate.frag +++ b/filters/pixelate/src/pixelate.frag @@ -1,40 +1,36 @@ -precision mediump float; +precision highp float; +in vec2 vTextureCoord; +out vec4 finalColor; -varying vec2 vTextureCoord; - -uniform vec2 size; +uniform vec2 uSize; uniform sampler2D uSampler; - -uniform vec4 filterArea; +uniform vec4 uInputSize; vec2 mapCoord( vec2 coord ) { - coord *= filterArea.xy; - coord += filterArea.zw; + coord *= uInputSize.xy; + coord += uInputSize.zw; return coord; } vec2 unmapCoord( vec2 coord ) { - coord -= filterArea.zw; - coord /= filterArea.xy; + coord -= uInputSize.zw; + coord /= uInputSize.xy; return coord; } -vec2 pixelate(vec2 coord, vec2 size) +vec2 pixelate(vec2 coord, vec2 uSize) { - return floor( coord / size ) * size; + return floor( coord / uSize ) * uSize; } void main(void) { vec2 coord = mapCoord(vTextureCoord); - - coord = pixelate(coord, size); - + coord = pixelate(coord, uSize); coord = unmapCoord(coord); - - gl_FragColor = texture2D(uSampler, coord); + finalColor = texture(uSampler, coord); } diff --git a/filters/pixelate/src/pixelate.wgsl b/filters/pixelate/src/pixelate.wgsl new file mode 100644 index 000000000..822dc7bfc --- /dev/null +++ b/filters/pixelate/src/pixelate.wgsl @@ -0,0 +1,53 @@ +struct PixelateUniforms { + uSize:vec2, +}; + +struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; + +@group(0) @binding(1) var uSampler: texture_2d; +@group(1) @binding(0) var pixelateUniforms : PixelateUniforms; + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @builtin(position) position: vec4 +) -> @location(0) vec4 { + let pixelSize: vec2 = pixelateUniforms.uSize; + let coord: vec2 = mapCoord(uv); + + var pixCoord: vec2 = pixelate(coord, pixelSize); + pixCoord = unmapCoord(pixCoord); + + return textureSample(uSampler, uSampler, pixCoord); +} + +fn mapCoord(coord: vec2 ) -> vec2 +{ + var mappedCoord: vec2 = coord; + mappedCoord *= gfu.inputSize.xy; + mappedCoord += gfu.outputFrame.xy; + return mappedCoord; +} + +fn unmapCoord(coord: vec2 ) -> vec2 +{ + var mappedCoord: vec2 = coord; + mappedCoord -= gfu.outputFrame.xy; + mappedCoord /= gfu.inputSize.xy; + return mappedCoord; +} + +fn pixelate(coord: vec2, size: vec2) -> vec2 +{ + return floor( coord / size ) * size; +} + diff --git a/filters/twist/src/twist.wgsl b/filters/twist/src/twist.wgsl index 1da9102fd..2eac3deba 100644 --- a/filters/twist/src/twist.wgsl +++ b/filters/twist/src/twist.wgsl @@ -3,6 +3,17 @@ struct TwistUniforms { uOffset:vec2, }; +struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; + @group(0) @binding(1) var uSampler: texture_2d; @group(1) @binding(0) var twistUniforms : TwistUniforms; @@ -14,6 +25,14 @@ fn mainFragment( return textureSample(uSampler, uSampler, unmapCoord(twist(mapCoord(uv)))); } +fn mapCoord(coord: vec2 ) -> vec2 +{ + var mappedCoord: vec2 = coord; + mappedCoord *= gfu.inputSize.xy; + mappedCoord += gfu.outputFrame.xy; + return mappedCoord; +} + fn unmapCoord(coord: vec2 ) -> vec2 { var mappedCoord: vec2 = coord; diff --git a/tools/demo/src/filters/pixelate.js b/tools/demo/src/filters/pixelate.js index f30f8c741..2a361656d 100644 --- a/tools/demo/src/filters/pixelate.js +++ b/tools/demo/src/filters/pixelate.js @@ -2,7 +2,7 @@ export default function () { this.addFilter('PixelateFilter', function (folder) { - folder.add(this.size, '0', 4, 40).name('size.x'); - folder.add(this.size, '1', 4, 40).name('size.y'); + folder.add(this, 'sizeX', 4, 40).name('size.x'); + folder.add(this, 'sizeY', 4, 40).name('size.y'); }); } diff --git a/tools/demo/src/index.js b/tools/demo/src/index.js index b4c2a112c..30eb46922 100644 --- a/tools/demo/src/index.js +++ b/tools/demo/src/index.js @@ -36,6 +36,7 @@ const main = async () => filters.bloom.call(app); filters.grayscale.call(app); filters.twist.call(app); + filters.pixelate.call(app); // filters.kawaseBlur.call(app); // TODO: Re-enable this in place of the above once v8 conversion is complete