Optimizing Point Lights

This would need to be tested on other low end integrated chipsets / m1 / ati / nv / intel / qualcomm etc. to be sure that it doesn’t catastrophically de-opt any of those targets, but even if that ends up being true, it might be workable with some kind of fallback… or have it behind an opt-in flag of some kind. renderer.optimizedLightingShaders = true, and let users enable it based on some GPU sniffing of some sort. Then the issue is more of a maintenance one, since it increases the code footprint and creates more paths that need to be maintained in the renderer. I don’t know what the calculus would be there…
Perhaps it could also be distributed as an addon that patches the shader chunks at init time? That might be a low risk approach to get it into the wild… and that doesn’t require any sign-on from maintainers since the maintenance burden is on the user.

1 Like

Hi Unsul !

Any chance you are willing to open source your clustered forward solution?
Also what do you think about making it compatible with WebGPU , see the performance difference here, I think you can achieve pure gpu compute performance

https://toji.github.io/webgpu-clustered-shading/

But I am curious to see how easy would it be to integrate into threejs in your experience? Did you have to have a custom fork of threejs or just a unique material that you shaded with your custom shader?

Thank you

1 Like

Hy @clibequilibrium

Any chance you are willing to open source your clustered forward solution?

I appreciate your interest in my work, I have no plans to opensource this work. I get asked this occasionally, and I get why. The main reason is trade-off between usefulness and competitive advantage. If I shared my work and it would be useful to many people - that would be good, but sharing my work will mean that I can’t charge for it anymore.

Over the years my engine has become more and more complex, I regularly rewrite parts of it to simplify where possible, but the key differences between my work and a lot of what’s out there is down to better basics. I have my own BitSet implementation that is very fast, I have my own BVH , my own HashMap, caches etc. Sharing my work would mean sharing all of those as well, and without them - the work that I do is going to be a lot less useful.

With those components, it’s too complex for a normal person to understand, and for an experienced engineer in this area - it’s would likely take too long to adapt my work to something that they would find useful. So, I don’t believe that my work would be “useful” to many people if I did share it. A few - maybe, but it would mostly be for commercial use, which would defeat the point of making it opensource.

Perhaps I’m wrong in my assumptions, but these are the conclusions I have reached.

Also what do you think about making it compatible with WebGPU , see the performance difference here, I think you can achieve pure gpu compute performance

I am a fan of Toji’s work. And I have looked quite closely to his implementation, it’s a solid piece of work I think, and for many people it would be sufficient. When it comes to WebGPU in general - my solution could be adapted to WebGPU, but it would take some work to do that.

Key deficiency of Toji’s approach, which is no fault of his really, it’s more to do with the simplicity that he went for - it’s that his solution scales in O(n*m) time, where n is number of lights and m is number of clusters. My solution scales in O(log(n)*m). Another, less obvious issue is number of supported lights. Toji’s code breaks when number of lights in a cluster goes above 255, that’s a high limit, but it’s a limit. My solution goes up to millions of lights per cluster, even if performance would be very poor. I think what Toji wrote is very reasonable, and it would serve you 99% of the time. I know for a fact that a number of high-profile AAA games were shipped with similar implementations as well, but the artist there had to be conscious of the limitations.

But I am curious to see how easy would it be to integrate into threejs in your experience?

What I wrote uses shader transformations, so it works with three.js materials without any issue. When it comes to three.js specifically, I think forking is not a good strategy, I have avoided doing so myself and instead tried to stretch the limits of what can be done without changing three.js itself.

3 Likes

Thank you for your reply !

I totally get your points and thank you for putting it this way. Could you steer me a little how did you approach extending threejs to support your use case. In other words, how were you able to replace lighting engine without forking the three js ? I am just starting with three js and mostly coming from C/C++ and C# land so it can take me some time, but I would really like to know the theory behind that.

Once again thank you for your time and the replies :raised_hands:

Hey @clibequilibrium ,

the basics are like so:

  1. We don’t add lights to the scene like we normally do, or use a separate representation that three.js would ignore. Something like class ForwardPlusLight extends Object3D{ ... }. This is necessary to prevent three.js from treating this as a normal light and running it through the normal shading. We must collect and manage these lights separately.
  2. We use a 3d texture to represent cluster information, this information is very limited, we only have a few channels, like RGB or RGBA. Let’s say you use Uint32 texture here, you can pack 3 uint32 values into each cell of your 3d texture, this is your cluster information.
  3. Since the 3d texture is limited in how much data we can store there per voxel/pixel, we need a separate data texture to store actual light list with all related attributes, such as color, radius, intensity etc. The 3d texture now stores indices into this new light texture.

There’s more to it, but that’s the basics. We then use shader rewriting via onBeforeCompile to inject these new textures into material’s uniforms, and add a bit of shader code to the fragment shader to process these new lights.

4 Likes

Thank you :palms_up_together:

1 Like