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 filters 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.