Closed cvpcasada closed 5 months ago
This morning I was trying to make this filter work (I am using the webgl
renderer). This is a version of the filter that works for webgl
:
import { Filter, GlProgram, PointData } from 'pixi.js';
import { vertex, wgslVertex } from "pixi-filters";
const fragment = `
precision highp float;
in vec2 vTextureCoord;
out vec4 finalColor;
uniform sampler2D uTexture;
uniform vec2 uVelocity;
uniform int uKernelSize;
uniform float uOffset;
uniform vec4 uInputSize;
const int MAX_KERNEL_SIZE = 2048;
// Notice:
// the perfect way:
// int kernelSize = min(uKernelSize, MAX_KERNELSIZE);
// BUT in real use-case , uKernelSize < MAX_KERNELSIZE almost always.
// So use uKernelSize directly.
void main(void)
{
vec4 color = texture(uTexture, vTextureCoord);
if (uKernelSize == 0)
{
finalColor = color;
return;
}
vec2 velocity = uVelocity / uInputSize.xy;
float offset = -uOffset / length(uVelocity) - 0.5;
int k = uKernelSize - 1;
for(int i = 0; i < MAX_KERNEL_SIZE - 1; i++) {
if (i == k) {
break;
}
vec2 bias = velocity * (float(i) / float(k) + offset);
color += texture(uTexture, vTextureCoord + bias);
}
finalColor = color / float(uKernelSize);
}
`;
/** Options for the MotionBlurFilter constructor. */
export interface MotionBlurFilterOptions {
velocity?: number[];
kernelSize?: number;
offset?: number;
}
export class MotionBlurFilter extends Filter {
public static readonly DEFAULT_OPTIONS: MotionBlurFilterOptions = {
velocity: [0, 0],
kernelSize: 5,
offset: 0,
};
public uniforms: {
uVelocity: [number, number];
uKernelSize: number;
uOffset: number;
};
private _kernelSize: number;
constructor(options?: MotionBlurFilterOptions) {
options = options ?? {};
options = { ...MotionBlurFilter.DEFAULT_OPTIONS, ...options };
// const gpuProgram = GpuProgram.from({
// vertex: {
// source: wgslVertex,
// entryPoint: 'mainVertex',
// },
// fragment: {
// source,
// entryPoint: 'mainFragment',
// },
// });
const glProgram = GlProgram.from({
vertex,
fragment,
name: 'motion-blur-filter',
});
super({
// gpuProgram,
glProgram,
resources: {
motionBlurUniforms: {
uVelocity: { value: options.velocity, type: 'vec2<f32>' },
uKernelSize: { value: options.kernelSize, type: 'i32' },
uOffset: { value: options.offset, type: 'f32' },
}
},
});
this.uniforms = this.resources.motionBlurUniforms.uniforms;
Object.assign(this, options);
}
/**
* Sets the velocity of the motion for blur effect
* This should be a size 2 array or an object containing `x` and `y` values, you cannot change types
* once defined in the constructor
* @default {x:0,y:0}
*/
get velocity(): [number, number] { return this.uniforms.uVelocity; }
set velocity(value: [number, number]) {
this.uniforms.uVelocity = value;
this._updateDirty();
}
get velocityX(): number { return this.velocity[0]; }
get velocityY(): number { return this.velocity[1]; }
/**
* The kernelSize of the blur filter. Must be odd number >= 5
* @default 5
*/
get kernelSize(): number { return this._kernelSize; }
set kernelSize(value: number) {
this._kernelSize = value;
this._updateDirty();
}
/**
* The offset of the blur filter
* @default 0
*/
get offset(): number { return this.uniforms.uOffset; }
set offset(value: number) { this.uniforms.uOffset = value; }
private _updateDirty() {
// The padding will be increased as the velocity and intern the blur size is changed
this.padding = (Math.max(Math.abs(this.velocityX), Math.abs(this.velocityY)) >> 0) + 1;
this.uniforms.uKernelSize = (this.velocityX !== 0 || this.velocityY !== 0) ? this._kernelSize : 0;
}
}
I changed the motionBlurUniforms.uKernelSize.type
to i32
, as it is an int in the shader, and updated the class so only [number, number]
are allowed for uVelocity
. Please note that I am very new at pixi.js and I did this by trial and error.
I haven't tested with other filters but
MotionBlurFilter
filter currently only works withwebgpu
renderer and notwebgl
Reproduction:
https://stackblitz.com/edit/vitejs-vite-bu1fxt?file=src%2FApp.tsx
Environment