Filters allow you to apply post processing effect on objects. These allow you to apply many different pixel based effects similar to filters in tools like photoshop, such as blur.

To apply filters to a display object we simply have to add it to the object's filters property which is an array. This means we can apply multiple filters to a single object.

sprite.filters = [];

PixiJS has some built in filters that we can apply to our objects, like BlurFilter and AlphaFilter. These each have a number of properties you can adjust. Their constructors can be accessed through the global PIXI.filters property. All the built in filterss can be found in their documentation.

let blurFilter = new PIXI.filters.BlurFilter();
sprite.filters.push(blurFilter);

Custom Filters

The real power of filters comes when you discover they are simply GLSL shaders and you can create your own custom filters. GLSL shaders are their own thing and writing them is a bit beyond the scope of this article, but I will show you a bit about how to get started writing your own filters for PixiJS.

You can create your own filter using the Filter class. This allows you to use GLSL shaders to define the filter. GLSL is its own shading language with a C-like syntax. It consists of two different types of shaders. A vertex shader, which defines the geometry and a fragment shader which draws the pixels and colors.

Vertex Shader

Because we're working in 2D and not concerned with 3D geometry we can simply re-use the default vertex shader provided by PixiJS. This can be found at https://github.com/pixijs/pixi.js/dev/packages/core/src/fragments/default.vert. If you're curious how this works you can play around with the values. Essentially it's creating a position for each vertex in our quad or display object. Using the camera's projection matrix provided by PixiJS and multiplying it by the vertex position in space. It also creates a variable vTextureCoord to pass into the fragment shader.

attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat3 projectionMatrix;
varying vec2 vTextureCoord;
void main(void)
{
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}

Fragment Shader

The fragment shader is where you'll want to do your primary work in PixiJS. Your basic shader will take the vTextureCoord we created in the vertex shader and use that along with uSampler, also provided by PixiJS, to create a texture2D. This is of type vec4 which represents the rgba values of each pixel. The fragment shader below will simply draw the display object as it would normally.

varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void)
{
    vec4 color = texture2D(uSampler, vTextureCoord);
    gl_FragColor = color;
}

Putting It Together

If we take the above shaders and wrap them in <script> tags with unique ids vertShader and fragShader. And respective types x-shader/x-vert and x-shader/x-frag. We can then access them as strings in javascript to create our custom filter. The Filter class takes 3 parameters, the vertex shader, the fragment shader, and uniforms. Uniforms is an object which contains variables we can pass into and update in our shaders.

const vShader = vertShader.innerText;
const fShader = fragShader.innerText;
const uniforms = {};

const myFilter = new PIXI.Filter(vShader, fShader, uniforms);

We can update our code to animate properties of our shader by creating a value inside our uniform object.

const uniforms = {
	delta: 0
};

This value can be updated in our animation loop.

let delta = 0;
function animate() {
    delta += 0.1;
    uniforms.delta = 0.5 + Math.sin(delta) * 0.5;
}

We can then update our fragment shader to use this uniform to manipulate pixel values.

varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform float delta;
void main(void)
{
    vec4 color = texture2D(uSampler, vTextureCoord);
    if (color.a != 0.0){
        color.r = delta;
        color.g -= delta;
        color.b -= delta;
    }
    gl_FragColor = color;
}

This code will apply a red filter on our sprite that will animate in and out. In our shader you can see we're also ignoring pixels where the alpha is zero. This allows transparent pixels to remain transparent.

See the Pen PixiJS Part 7: Filters by CJ Gammon (@cjgammon) on CodePen.



Click here to download the demo.