From 09308af128f7762a8af60706cf8160a148aac4e9 Mon Sep 17 00:00:00 2001 From: Arthur031221 Date: Thu, 1 Oct 2026 12:50:48 +0800 Subject: [PATCH] Also reset the vertex shader and UV_TRANSFORM define when clearing texture The first version of this fix only null-guarded the uvTransform uniform assignment. That left EffectPass out of sync: clearing texture does not fire a change event unless setVertexShader() or setChanged() runs, so the compound material kept compiling with the UV_TRANSFORM code path and the now-null uvTransform uniform. On the next render, three.js's WebGLUniforms would read .elements off that null value and crash, just not at the assignment site. Mirror the non-null branch: delete the UV_TRANSFORM define and call setVertexShader(null) when the texture is cleared, so EffectPass recompiles without the stale uniform reference. Added a test that builds a real EffectPass around the effect and checks the compiled vertex shader no longer references UvTransform after the texture is cleared. --- src/effects/TextureEffect.js | 9 ++++++++- test/effects/TextureEffect.js | 28 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/effects/TextureEffect.js b/src/effects/TextureEffect.js index 17df28793..bb03e3fe2 100644 --- a/src/effects/TextureEffect.js +++ b/src/effects/TextureEffect.js @@ -60,11 +60,12 @@ export class TextureEffect extends Effect { if(prevTexture !== value) { uniforms.get("map").value = value; - uniforms.get("uvTransform").value = value.matrix; defines.delete("TEXTURE_PRECISION_HIGH"); if(value !== null) { + uniforms.get("uvTransform").value = value.matrix; + if(value.matrixAutoUpdate) { defines.set("UV_TRANSFORM", "1"); @@ -89,6 +90,12 @@ export class TextureEffect extends Effect { } + } else { + + uniforms.get("uvTransform").value = null; + defines.delete("UV_TRANSFORM"); + this.setVertexShader(null); + } } diff --git a/test/effects/TextureEffect.js b/test/effects/TextureEffect.js index 09f2541ee..318b74011 100644 --- a/test/effects/TextureEffect.js +++ b/test/effects/TextureEffect.js @@ -1,5 +1,6 @@ import test from "ava"; -import { TextureEffect } from "postprocessing"; +import { Texture } from "three"; +import { EffectPass, TextureEffect } from "postprocessing"; test("can be created and destroyed", t => { @@ -16,3 +17,28 @@ test("can be updated", t => { t.notThrows(() => effect.update()); }); + +test("can clear an existing texture", t => { + + const effect = new TextureEffect({ texture: new Texture() }); + t.notThrows(() => { effect.texture = null; }); + t.is(effect.texture, null); + +}); + +test("recompiles its host pass after clearing an existing texture", t => { + + const texture = new Texture(); + texture.matrixAutoUpdate = true; + + const effect = new TextureEffect({ texture }); + const pass = new EffectPass(null, effect); + pass.initialize(null, false, undefined); + + t.true(pass.fullscreenMaterial.vertexShader.includes("UvTransform")); + + t.notThrows(() => { effect.texture = null; }); + + t.false(pass.fullscreenMaterial.vertexShader.includes("UvTransform")); + +});