diff --git a/src/image/pixels.js b/src/image/pixels.js index 7fa654db87..476c7ab5ec 100644 --- a/src/image/pixels.js +++ b/src/image/pixels.js @@ -307,6 +307,11 @@ p5.prototype._copyHelper = ( syMod = srcImage.height / 2; } if (dstImage._renderer && dstImage._renderer.isP3D) { + dstImage.push(); + dstImage.resetMatrix(); + dstImage.noLights(); + dstImage.blendMode(dstImage.BLEND); + dstImage.imageMode(dstImage.CORNER); p5.RendererGL.prototype.image.call( dstImage._renderer, srcImage, @@ -319,6 +324,7 @@ p5.prototype._copyHelper = ( dw, dh ); + dstImage.pop(); } else { dstImage.drawingContext.drawImage( srcImage.canvas, @@ -613,7 +619,14 @@ p5.prototype.filter = function(...args) { filterGraphicsLayer.filter(...args); // copy secondary webgl renderer back to original p2d canvas - this._renderer._pInst.image(filterGraphicsLayer, 0, 0); + this.copy( + // src + filterGraphicsLayer._renderer, + // src coods + 0, 0, this.width, this.height, + // dest coords + 0, 0, this.width, this.height + ); filterGraphicsLayer.clear(); // prevent feedback effects on p2d canvas } }; @@ -918,4 +931,4 @@ p5.prototype.updatePixels = function(x, y, w, h) { this._renderer.updatePixels(x, y, w, h); }; -export default p5; \ No newline at end of file +export default p5; diff --git a/src/webgl/p5.Framebuffer.js b/src/webgl/p5.Framebuffer.js index 7642ccf549..7003a12fc1 100644 --- a/src/webgl/p5.Framebuffer.js +++ b/src/webgl/p5.Framebuffer.js @@ -175,6 +175,7 @@ class Framebuffer { const prevCam = this.target._renderer._curCamera; this.defaultCamera = this.createCamera(); + this.filterCamera = this.createCamera(); this.target._renderer._curCamera = prevCam; this.draw(() => this.target.clear()); diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 613c50d7df..c4a3a37e32 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -1064,10 +1064,6 @@ p5.RendererGL = class RendererGL extends p5.Renderer { // Resize the framebuffer 'fbo' and adjust its pixel density if it doesn't match the target. this.matchSize(fbo, target); - // Set filterCamera for framebuffers. - if (target !== this) { - this.filterCamera = this.getFilterLayer().createCamera(); - } fbo.draw(() => this._pInst.clear()); // prevent undesirable feedback effects accumulating secretly. let texelSize = [ @@ -1084,6 +1080,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { // setup this._pInst.push(); this._pInst.noStroke(); + this._pInst.blendMode(constants.BLEND); // draw main to temp buffer this._pInst.shader(this.filterShader); @@ -1098,8 +1095,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this._pInst.clear(); this._pInst.shader(this.filterShader); this._pInst.noLights(); - this._pInst.rect(-target.width / 2, - -target.height / 2, target.width, target.height); + this._pInst.plane(target.width, target.height); }); // Vert pass: draw `tmp` to `fbo` @@ -1109,8 +1105,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this._pInst.clear(); this._pInst.shader(this.filterShader); this._pInst.noLights(); - this._pInst.rect(-target.width / 2, - -target.height / 2, target.width, target.height); + this._pInst.plane(target.width, target.height); }); this._pInst.pop(); @@ -1119,6 +1114,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { else { fbo.draw(() => { this._pInst.noStroke(); + this._pInst.blendMode(constants.BLEND); this._pInst.shader(this.filterShader); this.filterShader.setUniform('tex0', target); this.filterShader.setUniform('texelSize', texelSize); @@ -1127,8 +1123,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { // but shouldn't hurt to always set this.filterShader.setUniform('filterParameter', filterParameter); this._pInst.noLights(); - this._pInst.rect(-target.width / 2, -target.height / 2, - target.width, target.height); + this._pInst.plane(target.width, target.height); }); } @@ -1139,8 +1134,8 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this._pInst.push(); this._pInst.imageMode(constants.CORNER); this._pInst.blendMode(constants.BLEND); - this.filterCamera._resize(); - this._pInst.setCamera(this.filterCamera); + target.filterCamera._resize(); + this._pInst.setCamera(target.filterCamera); this._pInst.resetMatrix(); this._pInst.image(fbo, -this.width / 2, -this.height / 2, this.width, this.height); diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 09c10be158..cbbf15043b 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -120,18 +120,6 @@ suite('p5.RendererGL', function() { suite('filter shader', function() { setup(function() { - vert = `attribute vec3 aPosition; - attribute vec2 aTexCoord; - - varying vec2 vTexCoord; - - void main() { - vTexCoord = aTexCoord; - vec4 positionVec4 = vec4(aPosition, 1.0); - positionVec4.xy = positionVec4.xy * 2.0 - 1.0; - gl_Position = positionVec4; - }`; - frag = `precision highp float; varying vec2 vTexCoord; @@ -203,21 +191,21 @@ suite('p5.RendererGL', function() { test('filter accepts correct params', function() { myp5.createCanvas(5, 5, myp5.WEBGL); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); myp5.filter(myp5.POSTERIZE, 64); }); test('secondary graphics layer is instantiated', function() { let renderer = myp5.createCanvas(5, 5, myp5.WEBGL); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); assert.notStrictEqual(renderer.filterLayer, undefined); }); test('custom shader makes changes to main canvas', function() { myp5.createCanvas(5, 5, myp5.WEBGL); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.background('RED'); myp5.loadPixels(); let p1 = myp5.pixels.slice(); // copy before pixels is reassigned @@ -229,7 +217,7 @@ suite('p5.RendererGL', function() { test('secondary graphics layer matches main canvas size', function() { let g1 = myp5.createCanvas(5, 5, myp5.WEBGL); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); let g2 = g1.filterLayer; assert.deepEqual([g1.width, g1.height], [g2.width, g2.height]); @@ -239,7 +227,7 @@ suite('p5.RendererGL', function() { test('Filter graphics layer get resized in 2D mode', function () { let g1 = myp5.createCanvas(10, 10); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); myp5.resizeCanvas(5, 15); myp5.filter(s); @@ -253,7 +241,7 @@ suite('p5.RendererGL', function() { pg.circle(1, 1, 1); pg.loadPixels(); let p1 = pg.pixels.slice(); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); pg.loadPixels(); let p2 = pg.pixels; @@ -262,7 +250,7 @@ suite('p5.RendererGL', function() { test('Applying filter when a camera is applied', function () { myp5.createCanvas(50, 50, myp5.WEBGL); - let s1 = myp5.createShader(vert, frag); + let s1 = myp5.createFilterShader(frag); myp5.push(); myp5.background('RED'); myp5.camera(0, 0, 800); @@ -296,7 +284,7 @@ suite('p5.RendererGL', function() { c.curStrokeColor ]; let a1 = getShapeAttributes(); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); let a2 = getShapeAttributes(); console.log(a1); @@ -305,7 +293,7 @@ suite('p5.RendererGL', function() { test('geometries added after filter do not have shader applied', function() { myp5.createCanvas(4, 4, myp5.WEBGL); - let s = myp5.createShader(vert, frag); + let s = myp5.createFilterShader(frag); myp5.filter(s); myp5.fill('RED'); myp5.noStroke(); @@ -323,22 +311,6 @@ suite('p5.RendererGL', function() { assert.doesNotThrow(testCreateFilterShader, 'this should not throw'); }); - test('default vertex shader behaves the same as supplied vertex shader', function() { - myp5.createCanvas(4,4, myp5.WEBGL); - let s1 = myp5.createFilterShader(frag); - let s2 = myp5.createShader(vert, frag); - myp5.background('RED'); - myp5.filter(s1); - myp5.loadPixels(); - let p1 = myp5.pixels.slice(); - myp5.clear(); - myp5.background('RED'); - myp5.filter(s2); - myp5.loadPixels(); - let p2 = myp5.pixels; - assert.deepEqual(p1, p2); - }); - test('filter shader works on a p5.Graphics', function() { myp5.createCanvas(3,3, myp5.WEBGL); let pg = myp5.createGraphics(3,3, myp5.WEBGL); @@ -501,6 +473,59 @@ suite('p5.RendererGL', function() { let p2 = getPixels(); assert.notDeepEqual(p1,p2); }); + + suite('external context', function() { + const cases = [ + ['corner rectMode', () => myp5.rectMode(myp5.CORNER)], + ['corners rectMode', () => myp5.rectMode(myp5.CORNERS)], + ['center rectMode', () => myp5.rectMode(myp5.CENTER)], + ['corner imageMode', () => myp5.imageMode(myp5.CORNER)], + ['corners imageMode', () => myp5.imageMode(myp5.CORNERS)], + ['center imageMode', () => myp5.imageMode(myp5.CENTER)], + ['blend blendMode', () => myp5.blendMode(myp5.BLEND)], + ['add blendMode', () => myp5.blendMode(myp5.ADD)], + ['multiply blendMode', () => myp5.blendMode(myp5.MULTIPLY)] + ]; + + const getFilteredPixels = (mode, initialize, filterType) => { + myp5.createCanvas(10, 10, mode); + myp5.background(255); + if (mode === 'webgl') { + myp5.translate(-5, -5); + } + myp5.noStroke(); + myp5.fill(255, 0, 0); + myp5.rect(3, 3, 4, 4); + initialize(); + myp5.filter(filterType); + myp5.loadPixels(); + console.log(myp5._renderer.elt.toDataURL()); + const pixels = [...myp5.pixels]; + myp5.remove(); + return pixels; + }; + + for (const filterType of ['blur', 'invert']) { + suite(`${filterType} filter`, function() { + for (const mode of ['p2d', 'webgl']) { + suite(`${mode} mode`, function() { + let defaultPixels; + setup(() => { + defaultPixels = getFilteredPixels('p2d', () => {}, filterType); + }); + + for (const [name, initialize] of cases) { + test(name, function() { + const pixels = + getFilteredPixels(mode, initialize, filterType); + assert.deepEqual(pixels, defaultPixels); + }); + } + }); + } + }); + } + }); }); test('contours match 2D', function() {