-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Sky & Water: Improve physical accuracy and HDR support #32325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mrdoob
wants to merge
3
commits into
dev
Choose a base branch
from
sky-water-fixes
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We normally not use such low tone mapping exposure values in our demos. How is this setting compatible when combining the sky with a PBR/IBL scene that requires a higher exposure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out the sun is very bright 🙃
If we want to be physically accurate seems like it's the only option.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just fear this change might break existing usage and apps won't get the exposure right for their scenes.
What do you think of a new flag like
physicallyCorrectLightingthat enables the new code path?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually I don't think I've ever seen anyone using Sky in the wild... 🤔 Have you?
Only Shota but he did his own.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were some questions related to
THREE.Skyat the forum and stackoverflow in the past so the effect is used for sure e.g.:https://discourse.threejs.org/t/sky-shader-example/13653
https://discourse.threejs.org/t/sky-js-shader-example-why-doesnt-it-use-a-cube-map-camera/16602
TBH, I have no good feeling that the effect requires now such a special tone mapping exposure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll ask Claude to research how all the other engines handle this problem 👀
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I'll test asking https://jules.google/ now that it can be repoless.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's what Jules said:
Unreal, Unity, Filament, Godot
I have researched how other major rendering engines (Unreal Engine, Unity HDRP, Filament, Godot 4) handle the high intensity of the Sun and Sky.
Here is the summary of how they handle it:
1. They use Physical Units (High Values)
All modern PBR engines explicitly use physical units for lighting. They do not clamp or arbitrarily reduce the sun's intensity.
Unreal Engine: Uses Lux for Directional Lights. The default Sun intensity is ~100,000 - 120,000 Lux.
Unity HDRP: Uses Lux. The sun is consistently set to ~100,000 Lux.
Filament: Uses Lux. Sun is ~100,000 Lux.
Godot 4: Has moved to physical units, also using ~100,000 Lux for the sun.
2. They rely on Physical Camera Exposure (EV)
To handle these massive values, these engines rely on a physical camera model.
The scene is rendered with a camera Exposure Value (EV100) appropriate for daylight (typically EV 14-15).
Auto-Exposure (Eye Adaptation): Engines like Unreal and Unity heavily rely on auto-exposure to dynamically adjust the EV based on the scene's average luminance, allowing the camera to transition from bright outdoors (EV 15) to dark indoors (EV 5-8).
3. The "Dark Scene" Issue (Compatibility)
The concern raised in the PR ("mixing... with a PBR/IBL scene that requires a higher exposure") arises from mixing Physical units with Legacy/Arbitrary units.
In a fully physical workflow (Unreal/Unity), you cannot use "Intensity 1" lights alongside a "Intensity 100,000" Sun.
If you expose for the Sun (EV 15), an "Intensity 1" light becomes invisible.
The Solution in other engines: Users must set all scene lights to physical values. A standard 60W lightbulb is not Intensity 1, but ~800 Lumens (approx ~100 candela).
If the user wants to use the physical Sun, they must upgrade their other lights to physical intensities, or the scene will indeed look black.
4. Technical Note: "Pre-exposure" (Important for HalfFloat)
Google's Filament engine documentation notes a critical implementation detail relevant to HalfFloatType render targets (which the PR mentions):
FP16 (HalfFloat) maxes out at 65,504.
A Sun intensity of 100,000+ will clip if written directly to a HalfFloat buffer.
Solution: Filament "pre-exposes" lights. Before rendering, they multiply the Light Intensity by the Camera Exposure value: EffectiveLightIntensity = LightIntensity * Exposure (e.g., 100,000 * 0.0025 = 250).
This allows the high-dynamic-range lighting to fit comfortably within the precision and range limits of HalfFloat buffers while maintaining physical accuracy.
Conclusion for Three.js PR
The PR's approach of using 0.0025 exposure is physically correct for a Sun intensity of ~100k.
Compatibility: To support legacy scenes (where lights are Intensity ~1), you generally have two options:
Physical Mode: Require the user to use physical intensities for all lights (Sun ~100k, Bulbs ~800lm, Exposure ~0.0025).
Legacy Mode (Scaling): Provide a intensityScale on the Sky/Sun object to scale the 100k down to ~1 (e.g., 0.00001 scale) so it matches legacy lighting environments without changing global exposure.
The "Pre-exposure" optimization from Filament might be a good addition to the PR to ensure the 100k sun doesn't clip in the HalfFloat render target before tone mapping.
Blender, Maya, 3DS Max
I have researched how Blender and other major 3D software (Maya/Arnold, 3ds Max/V-Ray) handle sun intensity and exposure.
Here is the summary:
1. They Use Physical Units (High Intensity)
Just like the game engines, the major 3D software packages generally use physical units for the sun, resulting in high intensity values.
Blender:
The Sun Lamp strength is measured in Watts per square meter ($W/m^2$ ). A physically accurate value for sunlight is ~1000 $W/m^2$ (roughly 120,000 Lux).
The Nishita Sky texture (Blender's physical sky) outputs these physical values by default.
Note: The default Sun Lamp object in Blender starts at a strength of 1.0 (non-physical, very dim). Users are expected to increase this to ~1000 for physical accuracy, or use the Sky Texture which is physical by default.
Maya (Arnold): Uses physical sky models where intensity is often left at default (physical) or adjusted slightly (e.g., to 4-5), but relies on camera exposure.
3ds Max (V-Ray): The VRaySun is extremely bright by default. Users are explicitly taught to set the Physical Camera Exposure to roughly EV 13-15 (Sunny 16 rule) to prevent the image from being blown out.
2. They Rely on Exposure Controls
To handle the high dynamic range of a physical sun, all these tools rely on "Exposure" settings in the render or camera properties.
Blender: Uses Filmic (and recently AgX) color management. Users are advised to lower the Exposure value in the Color Management settings (often by ~6 stops) when using a physical sun/sky to get a properly exposed image.
V-Ray/Corona/Arnold: Strongly link the sun intensity to the Physical Camera settings (ISO, Shutter Speed, f-stop). If you don't use a physical camera with correct exposure, the render is white.
3. Compatibility with Standard Lights
The "dark scene" issue is standard across the industry when mixing physical sunlight with arbitrary point lights.
If you expose for the Sun (EV 15), a standard "Intensity 1" point light becomes invisible.
The Solution: Users must also set their artificial lights to physical values (e.g., a light bulb is not 1 Watt in Blender, but often needs to be set to its radiant flux in Watts, which might be much higher effectively depending on efficacy, or users crank values until it looks right relative to the sun).
Blender Add-ons: Popular add-ons like "Photographer" automatically handle this by letting users set exposure and light units physically (Lumens, Candelas) to match the physical sun.
Conclusion
The industry standard approach (Blender, V-Ray, Arnold) matches what game engines (Unreal, Unity) do:
Sun Intensity: High, physical values (~100k Lux / 1000$W/m^2$ ).
Exposure: Low exposure values (or High EV) to compensate.
Tone Mapping: Filmic/AgX/ACES to handle the range.
The "issue" of the sky being too bright is actually the correct physical behavior, and the solution is to expose for the sky and upgrade other lights to match.
Sounds like everyone relies on exposure.
I like the idea of doing something like:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good to know that other engines do it this way but still we must be prepared on questions from the user side.
How would you suggest users should migrate from the first to the second fiddle?
r182dev: https://jsfiddle.net/8f4bmc9u/
r182dev + this PR: https://jsfiddle.net/qd71h04t/
As you can see, the sky gets almost complete white with an exposure of
0.5. If I change the value to0.0025, the mesh gets black:https://jsfiddle.net/4w6q7khn/
I have tried to generate an environment map from the sky via
CubeCamerabut you don't get a proper result as well: https://jsfiddle.net/8zef3b1u/You must noticeably increase the exposure to see an effect which means the result is not consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't auto-exposure be performed in post-processing?
I think we will need parameters for eye adaptation and limits too.
Just a feeling about this: shouldn't there be less exposure at midday? Apparently, the same exposure is being used at sunset and at midday when the sunlight is most intense to show the same luminance intensity?