diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 2b8c556..88a88fc 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -501,7 +501,7 @@ if (this._filters) { renderer.objectRenderers.sprite.flush(); - renderer.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this, this._filters)//this._filterBlock); } if (this._mask) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 2b8c556..88a88fc 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -501,7 +501,7 @@ if (this._filters) { renderer.objectRenderers.sprite.flush(); - renderer.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this, this._filters)//this._filterBlock); } if (this._mask) diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager copy.js b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js new file mode 100644 index 0000000..2dac048 --- /dev/null +++ b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js @@ -0,0 +1,481 @@ +var WebGLManager = require('./WebGLManager'), + FilterTexture = require('../utils/FilterTexture'), + Shader = require('../shaders/Shader'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLFilterManager(renderer) +{ + WebGLManager.call(this, renderer); + + /** + * @member {any[]} + */ + this.filterStack = []; + + /** + * @member {any[]]} + */ + this.texturePool = []; + + /** + * @member {number} + */ + this.offsetX = 0; + + /** + * @member {number} + */ + this.offsetY = 0; + + // listen for context and update necessary buffers + var self = this; + this.renderer.on('context', function () + { + self.texturePool.length = 0; + self.initShaderBuffers(); + }); +} + +WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); +WebGLFilterManager.prototype.constructor = WebGLFilterManager; +module.exports = WebGLFilterManager; + +/** + * @param renderer {WebGLRenderer} + * @param buffer {ArrayBuffer} + */ +WebGLFilterManager.prototype.begin = function (buffer) +{ + this.defaultShader = this.renderer.shaderManager.defaultShader; + + this.width = this.renderer.projection.x * 2; + this.height = -this.renderer.projection.y * 2; + + this.buffer = buffer; +}; + +/** + * Applies the filter and adds it to the current filter stack. + * + * @param filterBlock {object} the filter that will be pushed to the current filter stack + */ +WebGLFilterManager.prototype.pushFilter = function (filterBlock) +{ + var gl = this.renderer.gl; + + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; + + var texture = this.texturePool.pop(); + if (!texture) + { + texture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; + + var localX = filterArea.x, + localY = filterArea.y; + + if (filterArea.x < 0) + { + filterArea.width += filterArea.x; + filterArea.x = 0; + } + + if (filterArea.y < 0) + { + filterArea.height += filterArea.y; + filterArea.y = 0; + } + + if (localX + filterArea.width > this.width) + { + filterArea.width = this.width - localX; + } + + if (localY + filterArea.height > this.height) + { + filterArea.height = this.height - localY; + } + + if (filterArea.width < 0) + { + filterArea.width = 0; + } + + if (filterArea.height < 0) + { + filterArea.height = 0; + } + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + // now restore the regular shader.. + // this.renderer.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + */ +WebGLFilterManager.prototype.popFilter = function () +{ + var gl = this.renderer.gl; + + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock._filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + if (filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if (!outputTexture) + { + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + outputTexture.resize(this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if (this.filterStack.length === 0) + { + gl.colorMask(true, true, true, true);//this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter._filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + // TODO need to remove these global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock._filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. should happen automatically now.. + // this.renderer.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** + * Applies the filter to the specified area. + * + * @param filter {AbstractFilter} the filter that needs to be applied + * @param filterArea {Texture} TODO - might need an update + * @param width {number} the horizontal range of the filter + * @param height {number} the vertical range of the filter + */ +WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) +{ + // use program + var gl = this.renderer.gl; + + var shader = filter.shaders[gl.id]; + + if (!shader) + { + shader = new Shader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + this.renderer.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if (filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderer.drawCount++; +}; + +/** + * Initialises the shader buffers. + * + */ +WebGLFilterManager.prototype.initShaderBuffers = function () +{ + var gl = this.renderer.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + +}; + +/** + * Destroys the filter and removes it from the filter stack. + * + */ +WebGLFilterManager.prototype.destroy = function () +{ + var gl = this.renderer.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) + { + this.texturePool[i].destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; +}; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 2b8c556..88a88fc 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -501,7 +501,7 @@ if (this._filters) { renderer.objectRenderers.sprite.flush(); - renderer.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this, this._filters)//this._filterBlock); } if (this._mask) diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager copy.js b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js new file mode 100644 index 0000000..2dac048 --- /dev/null +++ b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js @@ -0,0 +1,481 @@ +var WebGLManager = require('./WebGLManager'), + FilterTexture = require('../utils/FilterTexture'), + Shader = require('../shaders/Shader'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLFilterManager(renderer) +{ + WebGLManager.call(this, renderer); + + /** + * @member {any[]} + */ + this.filterStack = []; + + /** + * @member {any[]]} + */ + this.texturePool = []; + + /** + * @member {number} + */ + this.offsetX = 0; + + /** + * @member {number} + */ + this.offsetY = 0; + + // listen for context and update necessary buffers + var self = this; + this.renderer.on('context', function () + { + self.texturePool.length = 0; + self.initShaderBuffers(); + }); +} + +WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); +WebGLFilterManager.prototype.constructor = WebGLFilterManager; +module.exports = WebGLFilterManager; + +/** + * @param renderer {WebGLRenderer} + * @param buffer {ArrayBuffer} + */ +WebGLFilterManager.prototype.begin = function (buffer) +{ + this.defaultShader = this.renderer.shaderManager.defaultShader; + + this.width = this.renderer.projection.x * 2; + this.height = -this.renderer.projection.y * 2; + + this.buffer = buffer; +}; + +/** + * Applies the filter and adds it to the current filter stack. + * + * @param filterBlock {object} the filter that will be pushed to the current filter stack + */ +WebGLFilterManager.prototype.pushFilter = function (filterBlock) +{ + var gl = this.renderer.gl; + + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; + + var texture = this.texturePool.pop(); + if (!texture) + { + texture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; + + var localX = filterArea.x, + localY = filterArea.y; + + if (filterArea.x < 0) + { + filterArea.width += filterArea.x; + filterArea.x = 0; + } + + if (filterArea.y < 0) + { + filterArea.height += filterArea.y; + filterArea.y = 0; + } + + if (localX + filterArea.width > this.width) + { + filterArea.width = this.width - localX; + } + + if (localY + filterArea.height > this.height) + { + filterArea.height = this.height - localY; + } + + if (filterArea.width < 0) + { + filterArea.width = 0; + } + + if (filterArea.height < 0) + { + filterArea.height = 0; + } + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + // now restore the regular shader.. + // this.renderer.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + */ +WebGLFilterManager.prototype.popFilter = function () +{ + var gl = this.renderer.gl; + + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock._filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + if (filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if (!outputTexture) + { + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + outputTexture.resize(this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if (this.filterStack.length === 0) + { + gl.colorMask(true, true, true, true);//this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter._filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + // TODO need to remove these global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock._filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. should happen automatically now.. + // this.renderer.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** + * Applies the filter to the specified area. + * + * @param filter {AbstractFilter} the filter that needs to be applied + * @param filterArea {Texture} TODO - might need an update + * @param width {number} the horizontal range of the filter + * @param height {number} the vertical range of the filter + */ +WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) +{ + // use program + var gl = this.renderer.gl; + + var shader = filter.shaders[gl.id]; + + if (!shader) + { + shader = new Shader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + this.renderer.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if (filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderer.drawCount++; +}; + +/** + * Initialises the shader buffers. + * + */ +WebGLFilterManager.prototype.initShaderBuffers = function () +{ + var gl = this.renderer.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + +}; + +/** + * Destroys the filter and removes it from the filter stack. + * + */ +WebGLFilterManager.prototype.destroy = function () +{ + var gl = this.renderer.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) + { + this.texturePool[i].destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index 2dac048..87c99f0 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,5 +1,6 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), + RenderTarget = require('../utils/RenderTarget'); Shader = require('../shaders/Shader'); /** @@ -63,10 +64,29 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (filterBlock) +WebGLFilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; + var texture = this.texturePool.pop(); + + // get the bounds of the object.. + var bounds = target.filterArea || target.getBounds(); + + if (!texture) + { + texture = new RenderTarget(this.renderer.gl, bounds.width, bounds.height); + } + else + { + texture.resize(bounds.width, bounds.height); + } + + this.texture = texture; + + this.texture.activate(); + // this.texture. + /* var projection = this.renderer.projection; var offset = this.renderer.offset; @@ -159,7 +179,7 @@ gl.clear(gl.COLOR_BUFFER_BIT); filterBlock._glFilterTexture = texture; - +` */ }; /** @@ -168,6 +188,11 @@ */ WebGLFilterManager.prototype.popFilter = function () { + this.texturePool.push(this.texture); + + this.renderer.currentRenderTarget.activate(); + + /* var gl = this.renderer.gl; var filterBlock = this.filterStack.pop(); @@ -339,6 +364,7 @@ // return the texture to the pool this.texturePool.push(texture); filterBlock._glFilterTexture = null; + */ }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 2b8c556..88a88fc 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -501,7 +501,7 @@ if (this._filters) { renderer.objectRenderers.sprite.flush(); - renderer.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this, this._filters)//this._filterBlock); } if (this._mask) diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager copy.js b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js new file mode 100644 index 0000000..2dac048 --- /dev/null +++ b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js @@ -0,0 +1,481 @@ +var WebGLManager = require('./WebGLManager'), + FilterTexture = require('../utils/FilterTexture'), + Shader = require('../shaders/Shader'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLFilterManager(renderer) +{ + WebGLManager.call(this, renderer); + + /** + * @member {any[]} + */ + this.filterStack = []; + + /** + * @member {any[]]} + */ + this.texturePool = []; + + /** + * @member {number} + */ + this.offsetX = 0; + + /** + * @member {number} + */ + this.offsetY = 0; + + // listen for context and update necessary buffers + var self = this; + this.renderer.on('context', function () + { + self.texturePool.length = 0; + self.initShaderBuffers(); + }); +} + +WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); +WebGLFilterManager.prototype.constructor = WebGLFilterManager; +module.exports = WebGLFilterManager; + +/** + * @param renderer {WebGLRenderer} + * @param buffer {ArrayBuffer} + */ +WebGLFilterManager.prototype.begin = function (buffer) +{ + this.defaultShader = this.renderer.shaderManager.defaultShader; + + this.width = this.renderer.projection.x * 2; + this.height = -this.renderer.projection.y * 2; + + this.buffer = buffer; +}; + +/** + * Applies the filter and adds it to the current filter stack. + * + * @param filterBlock {object} the filter that will be pushed to the current filter stack + */ +WebGLFilterManager.prototype.pushFilter = function (filterBlock) +{ + var gl = this.renderer.gl; + + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; + + var texture = this.texturePool.pop(); + if (!texture) + { + texture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; + + var localX = filterArea.x, + localY = filterArea.y; + + if (filterArea.x < 0) + { + filterArea.width += filterArea.x; + filterArea.x = 0; + } + + if (filterArea.y < 0) + { + filterArea.height += filterArea.y; + filterArea.y = 0; + } + + if (localX + filterArea.width > this.width) + { + filterArea.width = this.width - localX; + } + + if (localY + filterArea.height > this.height) + { + filterArea.height = this.height - localY; + } + + if (filterArea.width < 0) + { + filterArea.width = 0; + } + + if (filterArea.height < 0) + { + filterArea.height = 0; + } + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + // now restore the regular shader.. + // this.renderer.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + */ +WebGLFilterManager.prototype.popFilter = function () +{ + var gl = this.renderer.gl; + + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock._filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + if (filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if (!outputTexture) + { + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + outputTexture.resize(this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if (this.filterStack.length === 0) + { + gl.colorMask(true, true, true, true);//this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter._filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + // TODO need to remove these global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock._filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. should happen automatically now.. + // this.renderer.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** + * Applies the filter to the specified area. + * + * @param filter {AbstractFilter} the filter that needs to be applied + * @param filterArea {Texture} TODO - might need an update + * @param width {number} the horizontal range of the filter + * @param height {number} the vertical range of the filter + */ +WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) +{ + // use program + var gl = this.renderer.gl; + + var shader = filter.shaders[gl.id]; + + if (!shader) + { + shader = new Shader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + this.renderer.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if (filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderer.drawCount++; +}; + +/** + * Initialises the shader buffers. + * + */ +WebGLFilterManager.prototype.initShaderBuffers = function () +{ + var gl = this.renderer.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + +}; + +/** + * Destroys the filter and removes it from the filter stack. + * + */ +WebGLFilterManager.prototype.destroy = function () +{ + var gl = this.renderer.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) + { + this.texturePool[i].destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index 2dac048..87c99f0 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,5 +1,6 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), + RenderTarget = require('../utils/RenderTarget'); Shader = require('../shaders/Shader'); /** @@ -63,10 +64,29 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (filterBlock) +WebGLFilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; + var texture = this.texturePool.pop(); + + // get the bounds of the object.. + var bounds = target.filterArea || target.getBounds(); + + if (!texture) + { + texture = new RenderTarget(this.renderer.gl, bounds.width, bounds.height); + } + else + { + texture.resize(bounds.width, bounds.height); + } + + this.texture = texture; + + this.texture.activate(); + // this.texture. + /* var projection = this.renderer.projection; var offset = this.renderer.offset; @@ -159,7 +179,7 @@ gl.clear(gl.COLOR_BUFFER_BIT); filterBlock._glFilterTexture = texture; - +` */ }; /** @@ -168,6 +188,11 @@ */ WebGLFilterManager.prototype.popFilter = function () { + this.texturePool.push(this.texture); + + this.renderer.currentRenderTarget.activate(); + + /* var gl = this.renderer.gl; var filterBlock = this.filterStack.pop(); @@ -339,6 +364,7 @@ // return the texture to the pool this.texturePool.push(texture); filterBlock._glFilterTexture = null; + */ }; diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index a537fe4..0a34e55 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -107,7 +107,7 @@ RenderTarget.prototype.activate = function() { - this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer ); } /** @@ -119,12 +119,14 @@ */ RenderTarget.prototype.resize = function(width, height) { + width = width | 0; + height = height | 0; + if(this.width === width && this.height === height) return; this.width = width; this.height = height; - this.projectionMatrix = new math.Matrix(); if(!this.root) @@ -132,10 +134,10 @@ var gl = this.gl; this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; + this.projectionMatrix.d = 1/height*2; this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + this.projectionMatrix.ty = -1; gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 2b8c556..88a88fc 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -501,7 +501,7 @@ if (this._filters) { renderer.objectRenderers.sprite.flush(); - renderer.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this, this._filters)//this._filterBlock); } if (this._mask) diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager copy.js b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js new file mode 100644 index 0000000..2dac048 --- /dev/null +++ b/src/core/renderers/webgl/managers/WebGLFilterManager copy.js @@ -0,0 +1,481 @@ +var WebGLManager = require('./WebGLManager'), + FilterTexture = require('../utils/FilterTexture'), + Shader = require('../shaders/Shader'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLFilterManager(renderer) +{ + WebGLManager.call(this, renderer); + + /** + * @member {any[]} + */ + this.filterStack = []; + + /** + * @member {any[]]} + */ + this.texturePool = []; + + /** + * @member {number} + */ + this.offsetX = 0; + + /** + * @member {number} + */ + this.offsetY = 0; + + // listen for context and update necessary buffers + var self = this; + this.renderer.on('context', function () + { + self.texturePool.length = 0; + self.initShaderBuffers(); + }); +} + +WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); +WebGLFilterManager.prototype.constructor = WebGLFilterManager; +module.exports = WebGLFilterManager; + +/** + * @param renderer {WebGLRenderer} + * @param buffer {ArrayBuffer} + */ +WebGLFilterManager.prototype.begin = function (buffer) +{ + this.defaultShader = this.renderer.shaderManager.defaultShader; + + this.width = this.renderer.projection.x * 2; + this.height = -this.renderer.projection.y * 2; + + this.buffer = buffer; +}; + +/** + * Applies the filter and adds it to the current filter stack. + * + * @param filterBlock {object} the filter that will be pushed to the current filter stack + */ +WebGLFilterManager.prototype.pushFilter = function (filterBlock) +{ + var gl = this.renderer.gl; + + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; + + var texture = this.texturePool.pop(); + if (!texture) + { + texture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; + + var localX = filterArea.x, + localY = filterArea.y; + + if (filterArea.x < 0) + { + filterArea.width += filterArea.x; + filterArea.x = 0; + } + + if (filterArea.y < 0) + { + filterArea.height += filterArea.y; + filterArea.y = 0; + } + + if (localX + filterArea.width > this.width) + { + filterArea.width = this.width - localX; + } + + if (localY + filterArea.height > this.height) + { + filterArea.height = this.height - localY; + } + + if (filterArea.width < 0) + { + filterArea.width = 0; + } + + if (filterArea.height < 0) + { + filterArea.height = 0; + } + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + // now restore the regular shader.. + // this.renderer.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + */ +WebGLFilterManager.prototype.popFilter = function () +{ + var gl = this.renderer.gl; + + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock._filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderer.projection; + var offset = this.renderer.offset; + + if (filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if (!outputTexture) + { + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); + } + outputTexture.resize(this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if (this.filterStack.length === 0) + { + gl.colorMask(true, true, true, true);//this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter._filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + // TODO need to remove these global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock._filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. should happen automatically now.. + // this.renderer.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** + * Applies the filter to the specified area. + * + * @param filter {AbstractFilter} the filter that needs to be applied + * @param filterArea {Texture} TODO - might need an update + * @param width {number} the horizontal range of the filter + * @param height {number} the vertical range of the filter + */ +WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) +{ + // use program + var gl = this.renderer.gl; + + var shader = filter.shaders[gl.id]; + + if (!shader) + { + shader = new Shader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + this.renderer.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if (filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderer.drawCount++; +}; + +/** + * Initialises the shader buffers. + * + */ +WebGLFilterManager.prototype.initShaderBuffers = function () +{ + var gl = this.renderer.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + +}; + +/** + * Destroys the filter and removes it from the filter stack. + * + */ +WebGLFilterManager.prototype.destroy = function () +{ + var gl = this.renderer.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) + { + this.texturePool[i].destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index 2dac048..87c99f0 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,5 +1,6 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), + RenderTarget = require('../utils/RenderTarget'); Shader = require('../shaders/Shader'); /** @@ -63,10 +64,29 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (filterBlock) +WebGLFilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; + var texture = this.texturePool.pop(); + + // get the bounds of the object.. + var bounds = target.filterArea || target.getBounds(); + + if (!texture) + { + texture = new RenderTarget(this.renderer.gl, bounds.width, bounds.height); + } + else + { + texture.resize(bounds.width, bounds.height); + } + + this.texture = texture; + + this.texture.activate(); + // this.texture. + /* var projection = this.renderer.projection; var offset = this.renderer.offset; @@ -159,7 +179,7 @@ gl.clear(gl.COLOR_BUFFER_BIT); filterBlock._glFilterTexture = texture; - +` */ }; /** @@ -168,6 +188,11 @@ */ WebGLFilterManager.prototype.popFilter = function () { + this.texturePool.push(this.texture); + + this.renderer.currentRenderTarget.activate(); + + /* var gl = this.renderer.gl; var filterBlock = this.filterStack.pop(); @@ -339,6 +364,7 @@ // return the texture to the pool this.texturePool.push(texture); filterBlock._glFilterTexture = null; + */ }; diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index a537fe4..0a34e55 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -107,7 +107,7 @@ RenderTarget.prototype.activate = function() { - this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer ); } /** @@ -119,12 +119,14 @@ */ RenderTarget.prototype.resize = function(width, height) { + width = width | 0; + height = height | 0; + if(this.width === width && this.height === height) return; this.width = width; this.height = height; - this.projectionMatrix = new math.Matrix(); if(!this.root) @@ -132,10 +134,10 @@ var gl = this.gl; this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; + this.projectionMatrix.d = 1/height*2; this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + this.projectionMatrix.ty = -1; gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index 63767bd..dcaacfe 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -147,7 +147,7 @@ this.textureBuffer = new RenderTarget(gl, this.width * this.resolution, this.height * this.resolution)//, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; - + this.render = this.renderWebGL; this.projection = new math.Point(this.width*0.5, -this.height*0.5); } @@ -278,14 +278,14 @@ var wt = displayObject.worldTransform; wt.identity(); - wt.translate(0, this.projection.y * 2); + // wt.translate(0, this.projection.y * 2); if (matrix) { wt.append(matrix); } - wt.scale(1,-1); + // wt.scale(1,-1); // setWorld Alpha to ensure that the object is renderer at full opacity displayObject.worldAlpha = 1; @@ -305,7 +305,7 @@ gl.viewport(0, 0, this.width * this.resolution, this.height * this.resolution); gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); - + if (clear) { this.textureBuffer.clear();