Custom Fragment Shader Filter in Pixi.js
This guide explains the step-by-step process of creating and implementing a custom fragment shader filter in Pixi.js. You will learn how to write the GLSL fragment code, define and update custom uniforms for dynamic effects, instantiate the filter within the Pixi.js framework, and apply it directly to your display objects to achieve unique visual rendering.
Step 1: Write the GLSL Fragment Shader
A fragment shader is written in GLSL (OpenGL Shading Language) and determines the color of each pixel. While Pixi.js provides a default vertex shader, you must provide the custom fragment shader.
Below is a standard GLSL ES 100 fragment shader that creates a simple animated wave distortion effect:
varying vec2 vTextureCoord; // The coordinates of the texture
uniform sampler2D uSampler; // The input texture (your sprite/image)
uniform float uTime; // Custom uniform to animate the effect over time
void main(void) {
vec2 uv = vTextureCoord;
// Apply a sine wave distortion to the X coordinate based on Y and Time
uv.x += sin(uv.y * 10.0 + uTime) * 0.01;
// Output the final pixel color
gl_FragColor = texture2D(uSampler, uv);
}Step 2: Define the Filter in JavaScript
To use the shader in Pixi.js, instantiate a PIXI.Filter
object. You will pass null or undefined as the
first argument to use the default vertex shader, followed by your custom
fragment shader string as the second argument, and an object containing
your custom uniforms as the third.
// Store the GLSL code in a template literal string
const fragmentShaderSource = `
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform float uTime;
void main(void) {
vec2 uv = vTextureCoord;
uv.x += sin(uv.y * 10.0 + uTime) * 0.01;
gl_FragColor = texture2D(uSampler, uv);
}
`;
// Define the uniforms with their starting values
const uniforms = {
uTime: 0.0
};
// Create the custom filter
const waveFilter = new PIXI.Filter(null, fragmentShaderSource, uniforms);Step 3: Apply the Filter to a Display Object
Filters in Pixi.js can be applied to any display object (such as a
Sprite, Container, or Graphics
object) by assigning them to the .filters array of that
object.
// Load a texture and create a sprite
const sprite = PIXI.Sprite.from('path/to/image.jpg');
// Apply the custom filter to the sprite
sprite.filters = [waveFilter];
// Add the sprite to your PixiJS stage
app.stage.addChild(sprite);Step 4: Animate the Uniforms
To make the filter dynamic and interactive, you must continuously update the uniforms inside the animation loop. Pixi.js provides an internal ticker that makes this process straightforward.
// Add a function to the PixiJS ticker to update the uTime uniform every frame
app.ticker.add((delta) => {
// Increment the time uniform based on the delta time
waveFilter.uniforms.uTime += 0.05 * delta;
});