Updates to lighting in three.js r155

Updating the light intensities for ambient and directional lights should be sufficient if you upgrade from r154 to r155. Since your baseline is r149, you probably also have to honor the changes related to color management. Read the following guideline for more details:

BTW: If you want to be on the safe side, upgrade the engine step-by-step (r149 - r150 - r151 and so on) to see when things start to change.

1 Like

Stepping through the different version like you suggest, it appears that some changes (but not all) happen at r152. Thereā€™s a lot of complicated documentation about a ā€œlinearā€ workflow, which confusingly seems to mean ā€œoperations are not linear any more, now they involve lots of exponential conversionsā€.

Anyway migrating will clearly be a major project, Iā€™ll just stick to r151.

FWIW some 20ish years ago, linear workflow appeared, and made a LOT of confusion in the 3d artist world.

Back in those days people were using GI mostly for architectural visualization. So you render a room with light bouncing and itā€™s all dark. You increase the gamma and itā€™s all washed out. You make the workflow linear, increase the gamma andā€¦ boom suddenly all the renderings start to look good.

To this day I wouldnā€™t be able to say which image should be power of 1/2.2, which should be 2.2.
I think gnomon even had a whole DVD dedicated to this topic. It is not easy to understand this area.

Also, choosing a version of three and sticking to it is a legitimate approach. Imagine if you had to ā€œtestā€ every version from like r70 to r150, no one could do that.

1 Like

I found the migration advice confusingā€¦ hereā€™s what worked for me:

Note: I am also using ColorManagement.enabled = false and renderer.outputColorSpace = LinearSRGBColorSpace; after upgrading to R152.

I could have done WebGLRenderer.useLegacyLights = false, but I donā€™t want to have to come back to this in R165.

I scaled the intensity for AmbientLight and SpotLight by Math.PI.
And for the SpotLight, I found I had to set decay = 0.0.

Now my scene renders as before in R154.

6 Likes

Thanks for the tip on decay! I could not get them to work. That should probably be adjusted or noted somewhere important. Perhaps it is already. Cheers.

I didnā€™t find a topic on Github but here are areas where itā€™s important that units are meters:

  • WebXR. Device data around IPD is SI/meters. Nausea results if game units arenā€™t meters, due to motion translation or perspective (to the best of my understanding).
  • Physically correct lighting ^.

My gameā€™s historical setting uses feet/miles. There is a lot of convenience in coding and debugging with game units equal to scene units. Other than the above, I havenā€™t encountered any problems, which is amazing.

I propose that documentation might be helpful; that these systems currently expect units to be meters. (Of course I would enjoy a global scaling constant.)

Ahā€¦ probably figure out what it is - maybe things in my scene are very far apart and the decay default is causing the light not to reach. However, it reached in previous versions. So not sure.

How does this compare to Babylon, Blender, Unity, and Unreal? Would like to get a sense of how their values work compared to Threeā€™s.

If I may guess, is Threeā€™s lighting model the one specified by GLTF?

Iā€™m wondering what sorts of conversions Iā€™d need to perform between engines if I were to, for example, make a framework that allows rendering with Three, Babylon, or PlayCanvas.

Ben Houston made a comparison of different enginesā€™ lighting units, and the required conversions:

The three.js physically-based lighting option predates glTF 2.0, but it does work out to match glTF and UE4.

5 Likes

Whatā€™s the rationale for making Ambient/Directional/HemisphereLight 100% intensity be Math.PI? Those lights are not physically based, and 0 to Math.PI seems to be less intuitive than 0 to 1.

My understanding is that all of those are physically-based.

It is just a flat-color ambient light. How would that be physical?

It could be anything. Why not 7.39 to represent 100%, for example? 1 for 100% would have been nice to keep, simpler to use: 0.6 instead of 0.6 * Math.PI everywhere.

Iā€™m still updating things, and I recently had to find/replace new AmbientLight(...) to new AmbientLight(... * Math.PI) which seems to add no practical value.

Another thing: the default value for ambient intensity is now 31.8% instead of 100% too. That could have been kept at 100% too.

Besides that, switching to physical lights (and soon removing legacy lights) is really great overall, gonna be clean to remove that code and have a single standard way to do things.

Iā€™m just sharing that I feel like some changes were not necessary, and hoping more unnecessary changes donā€™t happen because they cause us to spend unnecessary time on them.

AmbientLight is a source of diffuse lighting. Outdoor lighting on a cloudy day would be a good real-life example of diffuse lighting. I believe it also has physically-based units, but I do not recall what those are, and we should document that. Certainly it would be ā€¦ difficult ā€¦ to build an ā€œambient lightā€ as a household appliance, but thatā€™s not required.

Another thing: the default value for ambient intensity is now 31.8% instead of 100% too. That could have been kept at 100% too.

Iā€™m not sure what this means, sorry? The intensity default is still 1, the units are different.

You got it, because the default value is still 1, the actual intensity as a percentage is 31.8% (1/Math.PI), so the default amount of ambient light changed (despite the initial number being the same).

Sure, conceptually thatā€™s what it imitates. Mathematically, it is just a linear range from A to B, and 0 to 1 for A and B, for the case of simple ambient-like lights, was easier and more intuitive.

Can it be restored?

The thing is that the previous ambient light intensity often resulted in overexposed scenes if you didnā€™t use a grayscale ambient color and/or low intensity. I have actually experienced better ambient light color and intensity combinations with the new default.

1 Like

Yeah true, 100% is usually not what we want. Just thinking that it was unnecessary work.

But even if we leave it at say 50% (and maybe even default a non white color) I still think 0 to 1 would have been simple, and would not have hindered the engine from being physically based in the way it is now.

The new physically based default is awesome though. Great for the future.

Also keep in mind that ambient light intensity is not capped at 1 (or PI for that matter), larger values will be useful if youā€™re using physically-based units for other lights, and adjusting exposure.

I do wish we had better documentation and guidelines for physically-based lighting workflows. This isnā€™t purely a three.js issue ā€” creating the same rendered image in three.js that you see in Blender requires more than just correct physically-based light units. Tone mapping and exposure are also important, and harder to transfer.

2 Likes

Thanks for the tip on decay. That was a little confusing.

Being able to write a value of, say, 1.2 is easier than coming up with 3.77 in my mind, so Iā€™d end up writing 1.2 * Math.PI every time.

In basic scenes, 0 was black, 1 was full chosen color. Easy.

I just donā€™t see how writing * Math.PI for every ambient light is better. Anyway. Iā€™ve expressed the thought. Iā€™ll leave it alone!

Would be great!