/** * A Gaussian blur filter. With this filter, you can blur an area of a PIXI.DisplayObject. This cannot * be done with the PIXI.filters.BlurFilter (when you use the PIXI.filters.BlurFilter with * an filter area, all pixels outside of the area are not displayed). Attention: The area of * the filter is always in global scope, NOT relative to the PIXI.DisplayObject the filter * is assigned to! * * @example * // Create the app * const app = new PIXIApp({ * view: canvas, * width: 480, * height: 270, * transparent: false * }).setup().run() * * // Add a video sprite * const sprite = new PIXI.Sprite(PIXI.Texture.from("assets/blurfilter.mp4")) * sprite.width = app.size.width * sprite.height = app.size.height * app.scene.addChild(sprite) * * // Create the filter and assign it to the scene * const blurFilter = new BlurFilter(new PIXI.Rectangle(20, 20, 80, 60)) * app.scene.filters = [blurFilter] * * @class * @extends PIXI.Filter * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} shape The area where the blur effect should be applied to. Relative to the * canvas, NOT relative to the PIXI.DisplayObject where the blur effect is assigned to! * @param {number} [blur=50] The strength of the blur. */ export default class BlurFilter extends PIXI.Filter { constructor(shape, blur = 50) { super() const normalized = this.normalize(shape) this.tiltShiftXFilter = new TiltShiftXFilter(normalized, blur) this.tiltShiftYFilter = new TiltShiftYFilter(normalized, blur) } apply(filterManager, input, output) { let renderTarget = filterManager.getFilterTexture() this.tiltShiftXFilter.apply(filterManager, input, renderTarget) this.tiltShiftYFilter.apply(filterManager, renderTarget, output) filterManager.returnFilterTexture(renderTarget) // let renderTarget = filterManager.getRenderTarget(true) // this.tiltShiftXFilter.apply(filterManager, input, renderTarget) // this.tiltShiftYFilter.apply(filterManager, renderTarget, output) // filterManager.returnRenderTarget(renderTarget) } /** * The strength of the blur. * * @member {number} */ get blur() { return this.tiltShiftXFilter.blur } set blur(value) { this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value } /** * The blur shape. * * @member {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} */ get shape() { return this.tiltShiftXFilter.shape } set shape(value) { this.tiltShiftXFilter.shape = this.tiltShiftYFilter.shape = this.normalize(value) } /** * * @private * @param {PIXI.Rectangle|PIXI.Circle|PIXI.DisplayObject} value * @returns {Object} */ normalize(value) { let shape = null if (value instanceof PIXI.Circle) { shape = { type: 'circle', x: value.x, y: value.y, r: value.radius } } else if (value instanceof PIXI.Rectangle) { shape = { type: 'rectangle', x: value.x, y: value.y, width: value.width, height: value.height } } else { const bounds = value.getBounds() shape = { type: 'rectangle', x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height } } return shape } } /** * A TiltShiftAxisFilter. * * @class * @extends PIXI.Filter * @abstract * @private */ class TiltShiftAxisFilter extends PIXI.Filter { constructor(shape, blur) { const vertex = ` attribute vec2 aVertexPosition; attribute vec2 aTextureCoord; uniform mat3 projectionMatrix; varying vec2 vVertexPosition; varying vec2 vTextureCoord; void main(void) { vVertexPosition = aVertexPosition; vTextureCoord = aTextureCoord; gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); } ` const fragment = ` varying vec2 vVertexPosition; varying vec2 vTextureCoord; uniform sampler2D uSampler; uniform int shape; uniform vec4 rectangle; uniform vec3 circle; uniform float blur; uniform vec2 delta; uniform vec2 texSize; float random(vec3 scale, float seed) { return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); } void main(void) { bool inside = false; if (shape == 1) { inside = distance(vVertexPosition, circle.xy) <= circle.z; } else if (shape == 2) { inside = vVertexPosition.x >= rectangle.x && vVertexPosition.x <= rectangle.z && vVertexPosition.y >= rectangle.y && vVertexPosition.y <= rectangle.w; } if (inside) { vec4 color = vec4(0.0); float total = 0.0; float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * blur); sample.rgb *= sample.a; color += sample * weight; total += weight; } gl_FragColor = color / total; gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } else { gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); } } ` super(vertex, fragment) if (shape.type === 'circle') { this.uniforms.shape = 1 this.uniforms.circle = [shape.x, shape.y, shape.r] } else { this.uniforms.shape = 2 this.uniforms.rectangle = [shape.x, shape.y, shape.x + shape.width, shape.y + shape.height] } this.uniforms.blur = blur this.uniforms.delta = new PIXI.Point(0, 0) this.uniforms.texSize = new PIXI.Point(480, 270) this.updateDelta() } /** * The strength of the blur. * * @member {number} * @memberof PIXI.filters.TiltShiftAxisFilter# */ get blur() { return this.uniforms.blur } set blur(value) { this.uniforms.blur = value } /** * The blur shape. * * @member {PIXI.Rectangle} * @memberof PIXI.filters.TiltShiftAxisFilter# */ get shape() { if (this.uniforms.shape === 1) { const circle = this.uniforms.circle return new PIXI.Circle(circle[0], circle[1], circle[2]) } else { const rectangle = this.uniforms.rectangle return new PIXI.Rectangle(rectangle[0], rectangle[1], rectangle[2], rectangle[3]) } } set shape(value) { if (value.type === 'circle') { this.uniforms.shape = 1 this.uniforms.circle = [value.x, value.y, value.r] } else { this.uniforms.shape = 2 this.uniforms.rectangle = [value.x, value.y, value.x + value.width, value.y + value.height] } } } /** * A TiltShiftXFilter. * * @class * @extends PIXI.TiltShiftAxisFilter * @private */ class TiltShiftXFilter extends TiltShiftAxisFilter { /** * Updates the filter delta values. */ updateDelta() { this.uniforms.delta.x = 0.1 this.uniforms.delta.y = 0 } } /** * A TiltShiftYFilter. * * @class * @extends PIXI.TiltShiftAxisFilter * @private */ class TiltShiftYFilter extends TiltShiftAxisFilter { /** * Updates the filter delta values. */ updateDelta() { this.uniforms.delta.x = 0 this.uniforms.delta.y = 0.1 } }