PointLight/SpotLight: Change default decay to 2
Related issue: https://github.com/mrdoob/three.js/issues/23614#issuecomment-1099523811
Description
If we aim to make the lighting physically correct we have to switch the default decay to 2.
This PR is an attempt to do the change and see if there are any side effects.
/cc @bhouston @WestLangley @donmccurdy @Mugen87 @sunag
tl;dr βΒ this change looks good to me!
Summarizing legacy vs. physical attenuation:
- legacy: light attenuates from 100% to 0% linearly (
decay=1) or by inverse square (decay=2) with the ratio of object distance /light.distance, reaching 0% atlight.distancemeters. Because the range of the light is specified, and attenuation is distributed over that entire range, attenuation is not realistically based on physical distance. If eitherdecay=0ordistance=0, light does not attenuate. Distance is 0 by default, so light does not attenuate by default. - physical: light attenuates from 100% to 0% linearly (
decay=1) or by inverse square law (decay=2) with the object distance, never reaching exactly 0% unlesslight.distanceis specified. Whenlight.distanceis specified, it smoothly but quickly diminishes intensity to zero at that distance1, without affecting attenuation over the rest of the light's range. Ifdecay=0, light does not attenuate.
1 This is not physically correct, but is a useful tool to limit the otherwise infinite range of the light to something manageable for rendering performance in a scene with many lights. Unlike in the legacy mode, light.distance in physical mode has very minimal effect on the physical-correctness of attenuation within the range of the light.
In the legacy mode, attenuation is disabled by default and so the change to from decay=1 to decay=2 does not affect things much. Even with a distance, the light still has the same range, but at 50% of that range the light will now be at 25% strength rather than 50% strength.
In physical mode, attenuation is enabled by default, and the change in decay will more directly affect the perceived range of the light. This is not a dramatic difference but it is visible.
Given that most users are probably still using the legacy mode, this seems like as good a time as any to make the change. I've created a spreadsheet, below, to illustrate the changes. I approximated the quadratic dropoff at light.distance in physical mode as a hard cutoff for simplicity.

Source: Punctual light attenuation in three.js
NOTE: I've omitted it from the table, but increasing intensity by a factor of Math.PI is generally useful when switching from legacy to physical mode for the first time.
~~One other idea. This is obviously not physically realistic, but β if our ultimate goal is to delete the legacy mode entirely, the path of least migration resistance would be to reduce the default decay to 0, instead of increasing it to 2. π~~
Let me retract my last comment. I'd prefer to see this PR merged and decay=2 as the default. π
If one was making three.js now I would definitely have decay always default to 2 and enable physical lighting mode. Over at Threekit, this is what we do. It is only because of legacy that this isn't the default in Three.js in my opinion.
In physical lighting mode, the decay should be hardwired to 2, otherwise it is not physically-correct.
How to handle artist-friendly mode (the current default), is up for debate.
The original "physical lighting mode" didn't actually force people to be physical correct, rather it just changed the defaults and equations to be physically correct unless you changed things no? I think we just want the default to be physically correct while still allowing the user to move away to more stylized choices if they really want to. Thus I advocate changing all default behaviour to be physically correct and removing that mode.
I like the approach @bhouston describes.
Setting decay=2 as the default is a good change that can be made now, with or without further changes.
If/when we want to simplify further, we'll set physicallyCorrectLights=true as the default, and later remove the property entirely.
After that, if users want lights that decay in a non-realistic way, they can do so by increasing or decreasing .decay, with the understanding that .decay=2 (default) is the physically-correct value. I hope that's a reasonable accommodation that allows some flexibility to deal with artistic goals, performance constraints, etc.
Although this PR will affect user code, the migration task is simple. Users can restore the lighting by setting decay back to 1.
I vote to merge the PR for r147. I'll update docs, migration guide and check the examples. If some of them are too dark after this change, we can update the light's intensity, color or decay.
After chatting with @mrdoob this will land in r147 π .