Hi,
I would like to turn some lights on and off at runtime since some of my lights are off screen and I’m seeing a clear performance gain when disabling lights. However, when I set Object3D.visible to false
I get a huge lag spike. Presumably because three.js has to recompile all the shaders. Is there any way to turn lights on and off without a lag spike like this?
You could remove the light from the scene or change its color to 0x000000. Depending on the light type you can drop the intensity down to 0.0 as well.
Personally I remove the light from the scene when I need to.
1 Like
I believe removing lights causes a lag spike as well. Setting color or intensity to zero doesn’t, but it also doesn’t add any performance gain as far as I’m aware. So there’s no point in disabling them then.
1 Like
That’s correct. Removing a light from the scene as well as making it invisibile can cause the creation of a new THREEWebGLProgram
. And that means shader compilation which tends to be an expensive operation.
Yes. Setting the intensity to zero does not permanently improve the performance since the light is still part of the lighting computation in the shader. You just avoid the overhead of creating a new shader program. Sometimes the program already exists for the new lighting state (because it’s cached by the renderer) but in this case you still have to perform a program change (I mean you have to switch to the other shader programs) which also has a performance impact.
3 Likes
Is there any way to prevent the creation of a THREEWebGLProgram
when disabling lights? I can see why shader code needs to be modified when adding new lights to the scene. But I was hoping removing a light would just remove it from the shader uniform array instead of regenerating shader code.
No, that’s not possible. In most cases, it’s beneficial when the shader program is updated on a light removal since the overall performance is better in the long term. Apart from the initial overhead, less lighting computations are necessary in the shaders for all upcoming frames.
I digged around in the threejs source code a bit and from what I understand is that the lights are stored as an uniform array in the fragment shader. The shader iterates over the array in a for loop but this loop is unrolled. So that’s why the shader needs to be recompiled.
I’m wondering if it would be a good idea to add a new uniform with the current light count. And keep the (hardcoded) uniform array the same length, but then skip some of the light rendering based on the dynamic light count.
I’d like to implement this and do a pull request, but it depends on the amount of time I have. It sounds like the system I described is a lot of work to implement and I’m not sure whether or not this would improve performance at all.
Do you have any idea whether this would work or should I just give up the idea right away?
I don’t know. TBH, the solution sounds a bit like a hack. And I’m not sure the resulting benefits justify the additional complexity in the renderer. The performance might be worse in situations where you have static lights. And that’s probably the case in most scenarios.
Well, if you can manage to implement a compact solution and proof via benchmarks that the performance impact is acceptable or not noticeable at all, you can try it with a PR.
Cool, I’ll try that sometime.
I’m not very familiar with the threejs renderer code though I’ve learned some things already just now. But still I think it will be quite an effort to implement this so who knows if I’ll ever get around doing it.