HammeriteEyeAdaptation3D
Core: lighting. Inherits Node3D
Eye adaptation: what the player is standing in decides how bright the screen is.
A map's lighting spans a hundred to one - a sunlit courtyard reads 35 where a back room reads 0.3 - and no fixed exposure serves both. This walks the exposure towards what the light around the camera actually is, and publishes it as the hammerite_exposure global shader uniform (HammeriteShaderGlobals.exposure()), which every brush, decal and probe-lit material multiplies its light by.
It adapts DOWNWARDS only, by default: see adaptation_floor. An eye that recovers a dark room is the correct simulation of an eye and the wrong game.
It needs a map, or a map_3d to take one from. Without one it has nothing to read and leaves the exposure alone. It runs by itself every frame while auto_update is on; a game that has to place the update among its own turns that off and calls update_exposure().
It also publishes the light_curve, the other, fixed half of the same pipeline: exposure decides how bright the picture is, the curve decides what a light level is worth to begin with.
Properties
Methods
| Returns | Method |
|---|---|
void | adapt(light_level: float, delta: float) |
float | exposure_for(light_level: float) const |
float | sample_local_light_intensity() |
float | sample_look_light_intensity() |
void | update_exposure(delta: float) |
float | white_level() const |
Property descriptions
adaptation_floor
var adaptation_floor: float = 6.0
The dimmest light the eye will adapt TO. Anything darker is rendered as though it were this bright. This is what keeps a shadow a shadow. At its default it sits exactly at reference_light_level, so the exposure never rises above 1.0. Lower it to let dim rooms recover, and max_exposure then says how far.
auto_update
var auto_update: bool = true
Whether update_exposure() runs by itself every frame. Off for a game that calls it at a moment of its own choosing.
camera
var camera: Camera3D
The camera the light is read at and along. Null reads at this node, and skips the reading ahead.
current_exposure
var current_exposure: float
The exposure last published, moved by adapt(). Writing it does not publish it.
exposure_speed
var exposure_speed: float = 0.5
How fast the eye opens up again after bright light, as a lerp rate per second. 0.5 is about two seconds to settle.
light_curve
var light_curve: HammeriteLightCurve
What a light level is WORTH to the eye, published to every shader that lights from the bake. Exposure moves the whole picture at once, so it can carry a room up or down and can never make the dark part of one darker than the lit part. Null leaves the linear picture alone. See HammeriteLightCurve.
local_light_weight
var local_light_weight: float = 0.6
Weight of the light where the camera stands in the average the exposure adapts to.
look_distance
var look_distance: float = 512.0
How far ahead the exposure looks. An eye adapts to the room, not to the wall in front of the nose.
map
var map: HammeriteMap
The map whose probes are read, through HammeriteMap.light_level_at(). Null, with no map_3d, leaves the exposure alone.
map_3d
var map_3d: HammeriteMap3D
The HammeriteMap3D showing map, when there is one: the light is read in its space, so a map that has been moved or turned is read where it stands. Its map is the one read when map is null.
max_exposure
var max_exposure: float = 3.0
The highest exposure allowed; it only comes into it when adaptation_floor is set below reference_light_level.
min_exposure
var min_exposure: float = 0.05
How far the exposure may go down. The floor keeps a sunlit wall from being crushed to nothing.
reference_light_level
var reference_light_level: float = 6.0
The light level that renders at exposure 1.0 - what counts as a normally lit room. Measured against a bake: a sunlit hall runs about 35, a lit interior 6, a back room under 1.
stop_down_speed
var stop_down_speed: float = 2.0
How fast the eye stops down for MORE light. Faster than exposure_speed, as an eye is.
target_exposure
var target_exposure: float
Where current_exposure is heading: the exposure the light last read calls for.
target_light_weight
var target_light_weight: float = 0.4
Weight of the light where camera is looking in the average the exposure adapts to.
Method descriptions
adapt()
func adapt(light_level: float, delta: float) -> void
Walk current_exposure one step towards what light_level calls for, over delta seconds, and publish it.
exposure_for()
func exposure_for(light_level: float) -> float
What exposure a given light level calls for, before any adapting to it.
sample_local_light_intensity()
func sample_local_light_intensity() -> float
The light where the camera stands - or this node, with no camera - or -1 if there is nothing to ask. Through HammeriteMap.light_level_at().
sample_look_light_intensity()
func sample_look_light_intensity() -> float
The light where the player is LOOKING, or -1 if there is nothing to ask: sampled from the probes at the far end of the view, as far as the air the eye is in reaches.
update_exposure()
func update_exposure(delta: float) -> void
Walk the exposure towards the light around the camera, and publish it. Runs every frame by itself while auto_update is on.
white_level()
func white_level() -> float
How bright white comes out once the eye has adapted: 1, or more where min_exposure will not let it stop down as far as the light calls for. A game's bloom threshold follows this.