Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix binding of texture() #7668

Merged
merged 1 commit into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ class RendererGL extends Renderer {
if (!this._userEnabledStencil) {
this._internalDisable.call(this.GL, this.GL.STENCIL_TEST);
}

}

/**
Expand Down Expand Up @@ -1781,7 +1781,7 @@ class RendererGL extends Renderer {
if (!this._userEnabledStencil) {
this._internalDisable.call(this.GL, this.GL.STENCIL_TEST);
}

// Reset saved state
// this._userEnabledStencil = this._savedStencilTestState;
}
Expand Down Expand Up @@ -2349,6 +2349,7 @@ class RendererGL extends Renderer {

_setFillUniforms(fillShader) {
this.mixedSpecularColor = [...this.states.curSpecularColor];
const empty = this._getEmptyTexture();

if (this.states._useMetalness > 0) {
this.mixedSpecularColor = this.mixedSpecularColor.map(
Expand All @@ -2362,9 +2363,12 @@ class RendererGL extends Renderer {
fillShader.setUniform("uUseVertexColor", this._useVertexColor);
fillShader.setUniform("uMaterialColor", this.states.curFillColor);
fillShader.setUniform("isTexture", !!this.states._tex);
if (this.states._tex) {
fillShader.setUniform("uSampler", this.states._tex);
}
// We need to explicitly set uSampler back to an empty texture here.
// In general, we record the last set texture so we can re-apply it
// 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);
fillShader.setUniform("uTint", this.states.tint);

fillShader.setUniform("uHasSetAmbient", this.states._hasSetAmbient);
Expand Down
53 changes: 40 additions & 13 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ suite('p5.RendererGL', function() {

// It should be red
assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
})
});

test('textures remain bound after each draw call', function() {
myp5.createCanvas(20, 10, myp5.WEBGL);
myp5.background(255);
Expand All @@ -132,7 +133,7 @@ suite('p5.RendererGL', function() {
inputs.color = texture(myTex, inputs.texCoord);
return inputs;
}`
})
});

// Make a red texture
const tex = myp5.createFramebuffer();
Expand All @@ -154,7 +155,33 @@ suite('p5.RendererGL', function() {
// Both rectangles should be red
assert.deepEqual(myp5.get(5, 5), [255, 0, 0, 255]);
assert.deepEqual(myp5.get(15, 5), [255, 0, 0, 255]);
})
});

test('texture() does not remain bound', function() {
myp5.createCanvas(20, 10, myp5.WEBGL);
myp5.background(255);

const myShader = myp5.baseMaterialShader().modify({});

// Make a red texture
const tex = myp5.createFramebuffer();
tex.draw(() => myp5.background('red'));

myp5.shader(myShader);
const uSampler = myShader.samplers.find((s) => s.name === 'uSampler');

myp5.push();
myp5.texture(tex);
myp5.circle(0, 0, 20);
expect(uSampler.texture.isFramebufferTexture).toBeTruthy();
myp5.pop();

myp5.push();
// Texture should be unbound now
myp5.circle(0, 0, 20);
expect(uSampler.texture.isFramebufferTexture).toBeFalsy();
myp5.pop();
});
});

suite('default stroke shader', function() {
Expand Down Expand Up @@ -2711,48 +2738,48 @@ suite('p5.RendererGL', function() {
myp5.createCanvas(50, 50, myp5.WEBGL);
const gl = myp5._renderer.GL;
const isEnabled = gl.isEnabled(gl.STENCIL_TEST);

assert.equal(isEnabled, false);
assert.equal(myp5._renderer._userEnabledStencil, false);
}
);

test('Tracks when user manually enables stencil test',
function() {
myp5.createCanvas(50, 50, myp5.WEBGL);
const gl = myp5._renderer.GL;

gl.enable(gl.STENCIL_TEST);
assert.equal(myp5._renderer._userEnabledStencil, true);
assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
}
);

test('Tracks when user manually disables stencil test',
function() {
myp5.createCanvas(50, 50, myp5.WEBGL);
const gl = myp5._renderer.GL;

gl.enable(gl.STENCIL_TEST);
gl.disable(gl.STENCIL_TEST);

assert.equal(myp5._renderer._userEnabledStencil, false);
assert.equal(gl.isEnabled(gl.STENCIL_TEST), false);
}
);

test('Maintains stencil test state across draw cycles when user enabled',
function() {
let drawCalled = false;

myp5.createCanvas(50, 50, myp5.WEBGL);
const originalDraw = myp5.draw;

myp5.draw = function() {
drawCalled = true;
if (originalDraw) originalDraw.call(myp5);
};

const gl = myp5._renderer.GL;
gl.enable(gl.STENCIL_TEST);
assert.equal(gl.isEnabled(gl.STENCIL_TEST), true)
Expand All @@ -2764,7 +2791,7 @@ suite('p5.RendererGL', function() {
myp5.draw = originalDraw;
}
);

test('Internal clip operations preserve user stencil test setting',
function() {
myp5.createCanvas(50, 50, myp5.WEBGL);
Expand All @@ -2783,7 +2810,7 @@ suite('p5.RendererGL', function() {
assert.equal(gl.isEnabled(gl.STENCIL_TEST), true);
}
);

test('Internal clip operations do not enable stencil test for future draw cycles',
function() {
myp5.createCanvas(50, 50, myp5.WEBGL);
Expand Down