277 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * 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
 | 
						|
    }
 | 
						|
}
 |