onBeforeCompile() gets fired twice


For some reason onBeforeCompile gets triggered twice here for a single material. I’m surprised about this. Especially because there’s nothing about this on three.js docs.

Of course I did some debugging to see if there’s anything executed twice in my code, but that doesn’t seem to be the case. I’m only binding onBeforeCompile to a handler once, it’s only used on a single material and even the material is only used on just a single plane mesh.

I don’t see any logic in firing this twice as according to the docs onBeforeCompile should be executed for the whole program, not just for just a vertexShader or fragmentShader.

In the project I’m changing a material of an object imported with the gltfLoader.


// import scene from gltf with gtlfLoader

this.myObj = this.scn.getObjectByName('myObject');
this.myMat = this.myObj.material;

console.log('bind onBeforeCompile');
this.myMat.onBeforeCompile = (shader) => {
        console.log('onBeforeCompile', Date());

As you can see the onBeforeCompile is bound only once, but executed twice right after eachother (only 14 msec difference).

I don’t get why this gets fired twice. Is this intended behaviour or is something else going on? And if intended, why is that the case?

Thanks in advance!

Threejs version: 0.136.0

[edit] when binding the same material to another object in Blender too (so material bound to two objects) and export the scene to gltf and import it in threejs the onBeforeCompile still gets fired twice, so no additional times. So it’s independent to the amount of object-material binds.

[edit2] changing camera and renderer don’t have any influence on this either (which makes sense, but just in case I’ve tested it). And I just tested with just a single execution of renderer.render(); same result; double firing.

While debugging threeJs now, I see that the cacheKeys are different, so now it’s creating two (!!) programs for the same material:

When setting console.logs in three\build\three.module.js inside the getProgram() function threeJs is triggering the onBeforeCompile twice already there.

When comparing cache keys I noticed that it’s using the exact same program to create a cachekey, with just one number difference:

  • The first time the cache key is created and set to programs, it starts with
  • But for some reason the second time the cache key is the same (for the same material), except for one value; one value suddenly has changed for some reason I don’t get as I’m not changing the material myself:

The forelast value has changed from 2833 to 785, while the rest is completely the equal. This makes getProgram() to not recognize the key and creates a second program for the same material. And makes it trigger onBeforeCompile for a second time.

I have no clue what this value means and why this is suddenly changed and why it searches for the same cache key a second time eather. But although not sure, it’s starting to look like an issue in either the threejs base or the GLTF importer to me.

Any help would be appreciated before filing a bug on github. I’m definitely no pro at all on core threejs code internals like some of you guys are and I might still be missing something here.


Something changed in the material from one render to the next, so it has to re-compile in order to work properly.

The question is what has changed from one render to the next.

Are you running an infinite render loop? Do you have things loading in async while rendering?

I don’t think this is a bug, unless it is happening twice in a single render.