Talk to me about point lights and performance

So far in my game engine I’ve been keeping things simple, with a single directional light (sunlight). However, it would be nice to have point lights for local light sources - candles, lamps, fireplaces and so on, as well as dynamic lights for spells. This is particularly important if I plan to add day/night cycles into the game.

Up to this point, though, I’ve been hesitant to introduce anything like this because I don’t know what effect it would have on overall frame rate, and that’s partly because I really don’t understand how lighting works in three.js.

Static lights - such as candles - would generally have a small radius of influence and would not overlap with other point lights (in fact I could enforce this in the level editor). This would mean that even though a scene might have dozens of individual light sources, individual shader fragments would have at most two lights: the directional sunlight and the nearest point light.

Note that when I talk about a “scene” I’m talking about something that is very dynamic: the game engine supports worlds of effectively unlimited size, with scenery loaded asynchronously in the background as you travel across the terrain. This would mean, then, that the number of lights in the scene is constantly changing.

For things like spells, I can’t guarantee that the radius of effect would never overlap with other lights, but since the game’s combat is quasi-turn-based, only one spell is cast at a time. So that means that there would be three lights per fragment at most.

Then there is the question of shadows. I imagine that rendering a shadow map for dozens of individual point lights would be horribly slow. I supposed point lights could have shadows turned off, although that would not be as nice looking.

So what I’m asking is, before I start doing a bunch of work to integrate point-lighting into the engine, can someone give me a sense of whether this is even feasible, or will it have such a huge negative impact on framerate that it’s not worth pursuing?

2 Likes

IMHO, a game engine without point lights is incomplete, for an analogy, just imagine ThreeJS without point lights.

For the performance part, I think ThreeJS is already optimized and use the best practices to get the best out of the browser, you can always tweak it here and there but then you’ll find yourself in the realm of over engineering, spending time and energy where it’s not due. Then there is the user’s responsibility, I think you should let the user decide if the integrated amount of point lights is worth the performance penalty.

The integration should be a breeze, PointLight inherit the Object3D class, also unlike SpotLight and DirectionalLight doesn’t have a target, from my understanding your engine can already handle Object3D and cameras, then all you need is to add some new mechanism to handle the LightShadow class and its camera, which is the base for:

1 Like

In my city simulator I have decided to make these things configurable, so the users can choose performance or beauty. The slowest thing is a dynamic shadow (it is recalculated every frame and can push FPS down to 10). Static shadows are much faster, no shadows at all – are even fasterer (sic).

Also, I have config parameters for the number of lights, number of shadows, size of shadow maps, whether people cast shadows, etc.

If the most ugly situation (for highest performance) I have only one light (directional), always at the position of the camera and no shadows.

1 Like

From what I’ve seen some games and game engines fork three and implement a modern deferred rendering queue with clustered lighting. Otherwise I don’t think you could have more than 2-3.

For instance sougen https://twitter.com/onirenaud/status/1605192992117379072?s=46&t=Q54DhZomyZsSv5esF8GYyw

Or playcanvas https://twitter.com/playcanvas/status/1529108543349063681?s=46&t=Q54DhZomyZsSv5esF8GYyw

1 Like

I use them where I can, but with Ambient, Directional, Hemisphere in addition, I quickly start to run into a max lights shader issue, so I keep things simpler out of necessity.

Well, you are in luck! @orion_prime just published this excellent implementation of the r3f progressive shadows.

There is also the refined drie library, with some elegant shadow implementations for BakeShadows, ContactShadows and also AccumulativeShadows, it is react specific but you can always adapt it.

2 Likes

I wanted to follow up with the results of my experiments. Adding a dozen or so point lights into a scene did not noticeably affect performance in my case. However, that’s only if shadows are not enabled. I tried enabling them and the scene wouldn’t render at all, gave a WebGL error about not having enough texture units (this is on a Mac M1).

I think I can get by without point-light shadows, it’s mostly the shadow from sunlight (or moonlight) that is the most visually prominent.

Does it work if you use SpotLight or DirectionalLight instead of PointLight?

It’s not the type of light that is the issue, it’s the number of lights that have shadows enabled. DirectionalLight shadows work fine, and I’m sure PointLight shadows would work fine if I only had a small number of point lights. However, because my game supports huge worlds (dynamically loading in terrain and scenery as you move around the world) there’s no way to predict in advance how many point lights there will be.

In my test scene, I had a tavern with 8 candleholders and two fireplaces, each having a single point light. When I tried to enable shadows, OpenGL complained that there weren’t enough texture units (apparently I have 16). I’m guessing point light shadows are cube-mapped, so I’m not surprised that it needs more than one texture unit per light.

The reason to ask the question is that point lights are implemented as emitting light in 6 directions. I’m not sure whether their shadows also require more resources. So, if you replace your point lights by spot/directional lights, this might reduce the number of used texture units and make your Mac M1 happy. This experiment is very easy to conduct.

The sunlight is a perfect candidate to be replaced by a directional light, as you only care the light casted in one direction.

With shadows each point light will render the full scene additional six times.

Btw in some situations you can get away with using the environment map, which is practically for free, here are 100.000 „point lights“

1 Like