diff --git a/filters/rgb-split/src/RGBSplitFilter.ts b/filters/rgb-split/src/RGBSplitFilter.ts
index 7dd856802..d6f9ccd52 100644
--- a/filters/rgb-split/src/RGBSplitFilter.ts
+++ b/filters/rgb-split/src/RGBSplitFilter.ts
@@ -1,10 +1,29 @@
-import { vertex } from '@tools/fragments';
+import { vertex, wgslVertex } from '@tools/fragments';
import fragment from './rgb-split.frag';
-import { Filter, GlProgram } from 'pixi.js';
-import type { Point } from 'pixi.js';
+import source from './rgb-split.wgsl';
+import { Filter, GlProgram, GpuProgram, UniformGroup, Point } from 'pixi.js';
type Offset = [number, number] | Point;
+export interface RGBSplitFilterOptions
+{
+ /**
+ * The amount of offset for the red channel.
+ * @default [-10,0]
+ */
+ red: Offset;
+ /**
+ * The amount of offset for the green channel.
+ * @default [0,10]
+ */
+ green: Offset;
+ /**
+ * The amount of offset for the blue channel.
+ * @default [0,0]
+ */
+ blue: Offset;
+}
+
/**
* An RGB Split Filter.
* ![original](../tools/screenshots/dist/original.png)![filter](../tools/screenshots/dist/rgb.png)
@@ -16,13 +35,39 @@ type Offset = [number, number] | Point;
*/
export class RGBSplitFilter extends Filter
{
+ /** Default values for options. */
+ public static readonly DEFAULT_OPTIONS: RGBSplitFilterOptions = {
+ red: [-10, 0],
+ green: [0, 10],
+ blue: [0, 0],
+ };
+
/**
* @param {Point | number[]} [red=[-10,0]] - Red channel offset
* @param {Point | number[]} [green=[0, 10]] - Green channel offset
* @param {Point | number[]} [blue=[0, 0]] - Blue channel offset
*/
- constructor(red: Offset = [-10, 0], green: Offset = [0, 10], blue: Offset = [0, 0])
+ constructor(options?: RGBSplitFilterOptions)
{
+ options = { ...RGBSplitFilter.DEFAULT_OPTIONS, ...options };
+
+ const rgbSplitUniforms = new UniformGroup({
+ uRed: { value: new Float32Array(2), type: 'vec2' },
+ uGreen: { value: new Float32Array(2), type: 'vec2' },
+ uBlue: { 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,
@@ -30,53 +75,111 @@ export class RGBSplitFilter extends Filter
});
super({
+ gpuProgram,
glProgram,
- resources: {},
+ resources: {
+ rgbSplitUniforms
+ },
});
- // this.red = red;
- // this.green = green;
- // this.blue = blue;
+
+ this.red = options.red ?? [-10, 0];
+ this.green = options.green ?? [0, 10];
+ this.blue = options.blue ?? [0, 0];
}
/**
* Red channel offset.
- *
- * @member {Point | number[]}
+ * @default [-10,0]
+ */
+ get red(): Offset { return this.resources.rgbSplitUniforms.uniforms.uRed; }
+ set red(value: Offset)
+ {
+ if (value instanceof Point)
+ {
+ this.redX = value.x;
+ this.redY = value.y;
+
+ return;
+ }
+
+ this.resources.rgbSplitUniforms.uniforms.uRed = value;
+ }
+
+ /**
+ * Amount of x-axis offset for the red channel.
+ * @default -10
*/
- // get red(): Offset
- // {
- // return this.uniforms.red;
- // }
- // set red(value: Offset)
- // {
- // this.uniforms.red = value;
- // }
+ get redX(): number { return this.resources.rgbSplitUniforms.uniforms.uRed[0]; }
+ set redX(value: number) { this.resources.rgbSplitUniforms.uniforms.uRed[0] = value; }
+
+ /**
+ * Amount of y-axis offset for the red channel.
+ * @default 0
+ */
+ get redY(): number { return this.resources.rgbSplitUniforms.uniforms.uRed[1]; }
+ set redY(value: number) { this.resources.rgbSplitUniforms.uniforms.uRed[1] = value; }
/**
* Green channel offset.
- *
- * @member {Point | number[]}
+ * @default [0,10]
+ */
+ get green(): Offset { return this.resources.rgbSplitUniforms.uniforms.uGreen; }
+ set green(value: Offset)
+ {
+ if (value instanceof Point)
+ {
+ this.greenX = value.x;
+ this.greenY = value.y;
+
+ return;
+ }
+
+ this.resources.rgbSplitUniforms.uniforms.uGreen = value;
+ }
+
+ /**
+ * Amount of x-axis offset for the green channel.
+ * @default 0
+ */
+ get greenX(): number { return this.resources.rgbSplitUniforms.uniforms.uGreen[0]; }
+ set greenX(value: number) { this.resources.rgbSplitUniforms.uniforms.uGreen[0] = value; }
+
+ /**
+ * Amount of y-axis offset for the green channel.
+ * @default 10
+ */
+ get greenY(): number { return this.resources.rgbSplitUniforms.uniforms.uGreen[1]; }
+ set greenY(value: number) { this.resources.rgbSplitUniforms.uniforms.uGreen[1] = value; }
+
+ /**
+ * Blue channel offset.
+ * @default [0,0]
+ */
+ get blue(): Offset { return this.resources.rgbSplitUniforms.uniforms.uBlue; }
+ set blue(value: Offset)
+ {
+ if (value instanceof Point)
+ {
+ this.blueX = value.x;
+ this.blueY = value.y;
+
+ return;
+ }
+
+ this.resources.rgbSplitUniforms.uniforms.uBlue = value;
+ }
+
+ /**
+ * Amount of x-axis offset for the blue channel.
+ * @default 0
*/
- // get green(): Offset
- // {
- // return this.uniforms.green;
- // }
- // set green(value: Offset)
- // {
- // this.uniforms.green = value;
- // }
+ get blueX(): number { return this.resources.rgbSplitUniforms.uniforms.uBlue[0]; }
+ set blueX(value: number) { this.resources.rgbSplitUniforms.uniforms.uBlue[0] = value; }
/**
- * Blue offset.
- *
- * @member {Point | number[]}
+ * Amount of y-axis offset for the blue channel.
+ * @default 0
*/
- // get blue(): Offset
- // {
- // return this.uniforms.blue;
- // }
- // set blue(value: Offset)
- // {
- // this.uniforms.blue = value;
- // }
+ get blueY(): number { return this.resources.rgbSplitUniforms.uniforms.uBlue[1]; }
+ set blueY(value: number) { this.resources.rgbSplitUniforms.uniforms.uBlue[1] = value; }
}
diff --git a/filters/rgb-split/src/rgb-split.frag b/filters/rgb-split/src/rgb-split.frag
index 46fed0f09..c28d5d054 100644
--- a/filters/rgb-split/src/rgb-split.frag
+++ b/filters/rgb-split/src/rgb-split.frag
@@ -1,17 +1,18 @@
-precision mediump float;
-
-varying vec2 vTextureCoord;
+precision highp float;
+in vec2 vTextureCoord;
+out vec4 finalColor;
uniform sampler2D uSampler;
-uniform vec4 filterArea;
-uniform vec2 red;
-uniform vec2 green;
-uniform vec2 blue;
+uniform vec4 uInputSize;
+uniform vec2 uRed;
+uniform vec2 uGreen;
+uniform vec2 uBlue;
void main(void)
{
- gl_FragColor.r = texture2D(uSampler, vTextureCoord + red/filterArea.xy).r;
- gl_FragColor.g = texture2D(uSampler, vTextureCoord + green/filterArea.xy).g;
- gl_FragColor.b = texture2D(uSampler, vTextureCoord + blue/filterArea.xy).b;
- gl_FragColor.a = texture2D(uSampler, vTextureCoord).a;
+ float r = texture2D(uSampler, vTextureCoord + uRed/uInputSize.xy).r;
+ float g = texture2D(uSampler, vTextureCoord + uGreen/uInputSize.xy).g;
+ float b = texture2D(uSampler, vTextureCoord + uBlue/uInputSize.xy).b;
+ float a = texture2D(uSampler, vTextureCoord).a;
+ finalColor = vec4(r, g, b, a);
}
diff --git a/filters/rgb-split/src/rgb-split.wgsl b/filters/rgb-split/src/rgb-split.wgsl
new file mode 100644
index 000000000..d7d887785
--- /dev/null
+++ b/filters/rgb-split/src/rgb-split.wgsl
@@ -0,0 +1,31 @@
+struct RgbSplitUniforms {
+ uRed: vec2,
+ uGreen: vec2,
+ uBlue: vec3,
+};
+
+struct GlobalFilterUniforms {
+ uInputSize:vec4,
+ uInputPixel:vec4,
+ uuInputClamp: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 rgbSplitUniforms : RgbSplitUniforms;
+
+@fragment
+fn mainFragment(
+ @builtin(position) position: vec4,
+ @location(0) uv : vec2
+) -> @location(0) vec4 {
+ let r = textureSample(uSampler, uSampler, vTextureCoord + rgbSplitUniforms.uRed/uInputSize.xy).r;
+ let g = textureSample(uSampler, uSampler, vTextureCoord + rgbSplitUniforms.uGreen/uInputSize.xy).g;
+ let b = textureSample(uSampler, uSampler, vTextureCoord + rgbSplitUniforms.uBlue/uInputSize.xy).b;
+ let a = textureSample(uSampler, uSampler, vTextureCoord).a;
+ return vec4(r, g, b, a);
+}
diff --git a/tools/demo/src/index.js b/tools/demo/src/index.js
index 8ca4a000f..5aef3fe2c 100644
--- a/tools/demo/src/index.js
+++ b/tools/demo/src/index.js
@@ -39,6 +39,7 @@ const main = async () =>
filters.pixelate.call(app);
filters.glow.call(app);
filters.hslAdjustment.call(app);
+ filters.rgb.call(app);
// filters.kawaseBlur.call(app);
// TODO: Re-enable this in place of the above once v8 conversion is complete