diff --git a/src/core/p5.Renderer3D.js b/src/core/p5.Renderer3D.js index d83df0170d..363773befc 100644 --- a/src/core/p5.Renderer3D.js +++ b/src/core/p5.Renderer3D.js @@ -1501,7 +1501,11 @@ export class Renderer3D extends Renderer { // the next time a shader is used. However, the texture() function // works differently and is global p5 state. If the p5 state has // been cleared, we also need to clear the value in uSampler to match. - fillShader.setUniform("uSampler", this.states._tex || empty); + this._settingFillUniforms = true; + if (this.states._tex || !fillShader._userSetSampler) { + fillShader.setUniform("uSampler", this.states._tex || empty); + } + this._settingFillUniforms = false; fillShader.setUniform( "uTint", this.states.tint?._getRGBA([255, 255, 255, 255]) ?? [255, 255, 255, 255] diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index b12d9853cf..c39de0bd71 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -1103,6 +1103,10 @@ class Shader { return; } + if (uniformName === 'uSampler' && !this._renderer._settingFillUniforms) { + this._userSetSampler = true; + } + // In p5.strands-related code, where some of the code may be in // p5.webgpu.js instead of the main p5.js build, we generally use // duck typing instead of instanceof to avoid accidentally importing diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index abc661e2cd..850b0b4a97 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -1754,4 +1754,28 @@ visualTest('randomGaussian() in a fragment loop averages to the mean', (p5, scre screenshot(); }); }); + + visualTest( + 'user-set uSampler on custom shader is not overridden', + function(p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + + const myShader = p5.createFilterShader(`precision highp float; +uniform sampler2D uSampler; +varying vec2 vTexCoord; +void main() { + gl_FragColor = texture2D(uSampler, vTexCoord); +}`); + + const fbo = p5.createFramebuffer(); + fbo.draw(() => p5.background(255, 0, 0)); + + p5.shader(myShader); + p5.noStroke(); + myShader.setUniform('uSampler', fbo); + p5.plane(p5.width, p5.height); + + screenshot(); + } + ); }); diff --git a/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/000.png b/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/000.png new file mode 100644 index 0000000000..00d35a20a9 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/metadata.json b/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/user-set uSampler on custom shader is not overridden/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 079b8000f6..69ccf620da 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -208,6 +208,29 @@ suite('p5.RendererGL', function() { expect(uSampler.texture.isFramebufferTexture).toBeFalsy(); myp5.pop(); }); + + test('user-set uSampler on custom shader is not overridden', function() { + myp5.createCanvas(10, 10, myp5.WEBGL); + + const myShader = myp5.createFilterShader(`precision highp float; +uniform sampler2D uSampler; +varying vec2 vTexCoord; +void main() { + gl_FragColor = texture2D(uSampler, vTexCoord); +}`); + + const fbo = myp5.createFramebuffer(); + fbo.draw(() => myp5.background('red')); + + myp5.shader(myShader); + myp5.noStroke(); + myShader.setUniform('uSampler', fbo); + + myp5.plane(myp5.width, myp5.height); + + const pixel = myp5.get(5, 5); + assert.deepEqual(pixel, [255, 0, 0, 255]); + }); }); suite('default stroke shader', function() {