Skip to content

Commit

Permalink
Fix filter shaders when rectMode is applied; add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
davepagurek committed Dec 1, 2023
1 parent 0c174c6 commit 0f77c12
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 51 deletions.
17 changes: 15 additions & 2 deletions src/image/pixels.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -319,6 +324,7 @@ p5.prototype._copyHelper = (
dw,
dh
);
dstImage.pop();
} else {
dstImage.drawingContext.drawImage(
srcImage.canvas,
Expand Down Expand Up @@ -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
}
};
Expand Down Expand Up @@ -918,4 +931,4 @@ p5.prototype.updatePixels = function(x, y, w, h) {
this._renderer.updatePixels(x, y, w, h);
};

export default p5;
export default p5;
1 change: 1 addition & 0 deletions src/webgl/p5.Framebuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
19 changes: 7 additions & 12 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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);
Expand All @@ -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`
Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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);
});

}
Expand All @@ -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);
Expand Down
99 changes: 62 additions & 37 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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]);
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 0f77c12

Please sign in to comment.