@donmccurdy So after a great deal of trial and error, I came up with a workaround that prevents the flashing problem, but it is an ugly, ugly hack:
After I render a character portrait, I set a flag called fixLightsHack
. When this flag is set, I force the main render loop to do an extra call to render the main scene, but with shadows turned off. I then re-enable shadows and render the scene normally.
// Do an extra render pass, with shadows disabled - this forces
// a recalculation of all the lighting state.
if (this.fixLightsHack) {
this.fixLightsHack = false;
r.directionalLight.castShadow = false;
this.renderer.render(this.scene, this.camera);
r.directionalLight.castShadow = true;
}
this.renderer.render(this.scene, this.camera);
This means a frame rate drop when I render a portrait, but I don’t render portraits very often so I can live with it.
While investigating this, I discovered a few other things. First, let me explain the setup:
- Two WebGLRenderers - one for the main scene, and one for rendering portraits to a canvas element. Each renderer has its own separate scene and camera.
- The main scene uses a conventional animation / render loop via RAF.
- The portrait renderer is created lazily when needed and disposed immediately after each use.
- The main scene has a custom shader which does physical lighting using the standard three.js lighting chunks. The portrait renderer does not use this shader.
What I found:
- If both the main renderer scene and the portrait renderer scene have the same number of lights then rendering with the portrait renderer breaks the lighting in the main renderer. In this case “breaks” means that the custom shader renders everything as unlit.
- If the portrait renderer has fewer lights than the main scene, then rendering a portrait causes the main renderer to crash.
The crash is in the three.js material upload()
function. The reason is because the uniform variable direcrionalShadowMatrix
is undefined
, and the three.js code is expecting it to be an array.
This only happens with my custom shader, not with other materials. However, I don’t think its the fault of the custom shader since it’s not doing anything unusual with lights.
By doing an extra render pass, and flipping the state of the castShadow
property, it forces three.js to recalculate all of the light-related uniforms, which fixes the bug.
I tried your suggestions of playing with encoding and environment maps, but they did not solve the problem.