diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 7935255b49..cd3daf823f 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -18,6 +18,28 @@ class Renderer2D extends p5.Renderer{ this._pInst._setProperty('drawingContext', this.drawingContext); } + getFilterGraphicsLayer() { + // create hidden webgl renderer if it doesn't exist + if (!this.filterGraphicsLayer) { + // the real _pInst is buried when this is a secondary p5.Graphics + const pInst = + this._pInst instanceof p5.Graphics ? + this._pInst._pInst : + this._pInst; + + // create secondary layer + this.filterGraphicsLayer = + new p5.Graphics( + this.width, + this.height, + constants.WEBGL, + pInst + ); + } + + return this.filterGraphicsLayer; + } + _applyDefaults() { this._cachedFillStyle = this._cachedStrokeStyle = undefined; this._cachedBlendMode = constants.BLEND; diff --git a/src/image/pixels.js b/src/image/pixels.js index eef7fb5171..93ae5b84ac 100644 --- a/src/image/pixels.js +++ b/src/image/pixels.js @@ -8,7 +8,6 @@ import p5 from '../core/main'; import Filters from './filters'; import '../color/p5.Color'; -import * as constants from '../core/constants'; /** * An array containing the color of each pixel on the canvas. Colors are @@ -544,6 +543,15 @@ p5.prototype._copyHelper = ( * */ +/** + * @method getFilterGraphicsLayer + * @private + * @returns {p5.Graphics} + */ +p5.prototype.getFilterGraphicsLayer = function() { + return this._renderer.getFilterGraphicsLayer(); +}; + /** * @method filter * @param {Constant} filterType @@ -560,7 +568,7 @@ p5.prototype.filter = function(...args) { let { shader, operation, value, useWebGL } = parseFilterArgs(...args); // when passed a shader, use it directly - if (shader) { + if (this._renderer.isP3D && shader) { p5.RendererGL.prototype.filter.call(this._renderer, shader); return; } @@ -586,27 +594,11 @@ p5.prototype.filter = function(...args) { // when this is P2D renderer, create/use hidden webgl renderer else { - // create hidden webgl renderer if it doesn't exist - if (!this.filterGraphicsLayer) { - // the real _pInst is buried when this is a secondary p5.Graphics - const pInst = - this._renderer._pInst instanceof p5.Graphics ? - this._renderer._pInst._pInst : - this._renderer._pInst; - - // create secondary layer - this.filterGraphicsLayer = - new p5.Graphics( - this.width, - this.height, - constants.WEBGL, - pInst - ); - } + const filterGraphicsLayer = this.getFilterGraphicsLayer(); // copy p2d canvas contents to secondary webgl renderer // dest - this.filterGraphicsLayer.copy( + filterGraphicsLayer.copy( // src this._renderer, // src coods @@ -617,11 +609,11 @@ p5.prototype.filter = function(...args) { //clearing the main canvas this._renderer.clear(); // filter it with shaders - this.filterGraphicsLayer.filter(operation, value); + filterGraphicsLayer.filter(...args); // copy secondary webgl renderer back to original p2d canvas - this._renderer._pInst.image(this.filterGraphicsLayer, 0, 0); - this.filterGraphicsLayer.clear(); // prevent feedback effects on p2d canvas + this._renderer._pInst.image(filterGraphicsLayer, 0, 0); + filterGraphicsLayer.clear(); // prevent feedback effects on p2d canvas } }; diff --git a/src/webgl/material.js b/src/webgl/material.js index 849e901a63..822a861112 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -204,13 +204,13 @@ p5.prototype.createShader = function(vertSrc, fragSrc) { * Creates a new p5.Shader using only a fragment shader, as a convenience method for creating image effects. * It's like createShader() but with a default vertex shader included. * - * createFilterShader() is intended to be used along with filter() for filtering the contents of a canvas in WebGL mode. + * createFilterShader() is intended to be used along with filter() for filtering the contents of a canvas. * A filter shader will not be applied to any geometries. * * The fragment shader receives some uniforms: * - `sampler2D tex0`, which contains the canvas contents as a texture - * - `vec2 canvasSize`, which is the width and height of the canvas - * - `vec2 texelSize`, which is the size of a pixel (`1.0/width`, `1.0/height`) + * - `vec2 canvasSize`, which is the p5 width and height of the canvas (not including pixel density) + * - `vec2 texelSize`, which is the size of a physical pixel including pixel density (`1.0/(width*density)`, `1.0/(height*density)`) * * For more info about filters and shaders, see Adam Ferriss' repo of shader examples * or the introduction to shaders page. @@ -278,7 +278,6 @@ p5.prototype.createShader = function(vertSrc, fragSrc) { * */ p5.prototype.createFilterShader = function(fragSrc) { - this._assert3d('createFilterShader'); p5._validateParameters('createFilterShader', arguments); let defaultVertV1 = ` uniform mat4 uModelViewMatrix; @@ -322,7 +321,17 @@ p5.prototype.createFilterShader = function(fragSrc) { `; let vertSrc = fragSrc.includes('#version 300 es') ? defaultVertV2 : defaultVertV1; const shader = new p5.Shader(this._renderer, vertSrc, fragSrc); - shader.ensureCompiledOnContext(this._renderer.getFilterGraphicsLayer()); + if (this._renderer.isP3D) { + shader.ensureCompiledOnContext(this._renderer.getFilterGraphicsLayer()); + } else { + // In 2D mode, the image is copied to a WebGL p5.Graphics, and then the + // filter is applied there, which has its own graphic for running the + // shader. This may be simplified in the future by using framebuffers on + // a WebGL canvas instead of separate graphics. + shader.ensureCompiledOnContext( + this._renderer.getFilterGraphicsLayer().getFilterGraphicsLayer() + ); + } return shader; }; diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index 587247cd1f..0366df75e4 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -114,6 +114,18 @@ p5.Shader = class { * @param {p5|p5.Graphics} context The graphic or instance to copy this shader to. * Pass `window` if you need to copy to the main canvas. * @returns {p5.Shader} A new shader on the target context. + * + * @example + *
+ * let graphic = createGraphics(200, 200, WEBGL);
+ * let graphicShader = graphic.createShader(vert, frag);
+ * graphic.shader(graphicShader); // Use graphicShader on the graphic
+ *
+ * let mainShader = graphicShader.copyToContext(window);
+ * shader(mainShader); // Use `mainShader` on the main canvas
+ *
+ *