Noise-driven fire light rig with bounce, core, embers and glow
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Dynamic fire light
|
||||
|
||||
`scripts/fire_light.gd` replaces the old single light with sinusoidal flicker. One
|
||||
noise signal now drives the light rig **and** the flame, so brightness, colour, shadow
|
||||
sway and flame height pulse together.
|
||||
|
||||
## Research
|
||||
|
||||
Perplexity answered this time (it had been unavailable in earlier sessions), and the
|
||||
official documentation was checked in parallel:
|
||||
|
||||
- Godot renderer comparison table — Compatibility is the only method available for the
|
||||
web target. It supports 8 OmniLights per mesh by default, glow, depth/height fog and
|
||||
SSAO, and it does **not** support volumetric fog, light projector textures, PCSS,
|
||||
SSIL, SDFGI, decals or CompositorEffects post-processing. Lights **with** shadows are
|
||||
drawn in extra passes and blended in sRGB, so they differ in appearance and dominate
|
||||
the cost: https://docs.godotengine.org/en/stable/3d/overview_of_renderers.html
|
||||
- Faking global illumination — for positional lights, add a second OmniLight3D as a
|
||||
child of the main one with +25/50% range, shadows off, energy 10-40% and specular 0:
|
||||
https://docs.godotengine.org/en/stable/tutorials/3d/global_illumination/faking_global_illumination.html
|
||||
- FastNoiseLite (`get_noise_1d`, Perlin, seed):
|
||||
https://docs.godotengine.org/en/stable/classes/class_fastnoiselite.html
|
||||
- Key advice from Perplexity: continuous noise instead of per-frame randomness, fixed
|
||||
`omni_range`, frame-rate independent smoothing, amplitude 15-30%, colour variation
|
||||
smaller than brightness, `omni_attenuation` ~2 for a physical falloff, one shadowed
|
||||
light only, and profile illumination and shadows separately.
|
||||
- Community: hard on/off flicker makes the lit area visibly "expand" after each switch,
|
||||
so the light is never toggled. Particles are lit inconsistently by a light placed
|
||||
inside the same particle system, which is why the flame stays unshaded and luminous on
|
||||
its own (the light only lights the room).
|
||||
|
||||
## Implementation
|
||||
|
||||
| Element | Value | Reason |
|
||||
| --- | --- | --- |
|
||||
| Main light | energy 1.9-2.8, range **fixed** 10, attenuation 1.6, shadows **on** (cube) | the only shadow caster; animating the range makes the boundary breathe |
|
||||
| Noise | FastNoiseLite Perlin, seed 20261011, 0.8 Hz (weight 0.75) + 7 Hz (0.25), gain 1.6, response 15 | smooth, deterministic, frame-rate independent, no strobing |
|
||||
| Colour drift | green 0.36-0.42, blue 0.07-0.12 | deliberately smaller than the brightness swing |
|
||||
| Sway | ±4 cm at ~1.2 Hz, main light only | makes the shadows move; kept subtle |
|
||||
| Bounce light | child, range 14 (=+40%), energy 20%, shadows off, specular 0, attenuation 1.5 | documented fake-GI recipe; no indirect lighting exists in Compatibility |
|
||||
| Core light | range 3.5, energy 45%, shadows off, 45 cm below the main light | hot glow at the flame |
|
||||
| Embers | 8 unshaded additive particles, 1.8 s | cheapest large realism gain |
|
||||
| Flame | quad height ±8%, material brightness ±25% | visibly breathes with the light |
|
||||
| Glow | enabled, intensity 0.45, blend additive, `hdr_threshold` **1.3**, levels 2-4 | stands in for the missing volumetric fog; a high threshold keeps the amber HUD from blooming |
|
||||
|
||||
Total: 6 omni lights in the room (2 desk lamps, 3 fire, 1 muzzle flash), only one with
|
||||
shadows, against the default limit of 8 per mesh.
|
||||
|
||||
## Measured cost (desktop Radeon 780M, GL Compatibility, 1280x720, vsync off)
|
||||
|
||||
| State | FPS | Frame time |
|
||||
| --- | --- | --- |
|
||||
| Fire rig active | 262.6 | 3.8 ms |
|
||||
| Fire lights hidden | 250.7 | 4.0 ms |
|
||||
| Shadows off | 588.4 | 1.7 ms |
|
||||
|
||||
The three fire lights are inside the measurement noise; the single shadow-casting light
|
||||
costs about 2.1 ms, i.e. more than half the frame. That matches the documentation and is
|
||||
the intended trade: the shadow is what makes the fire feel present. On a phone the same
|
||||
ratio is expected to be heavier, and this workstation cannot measure it. To shed the cost
|
||||
on weak devices, in this order: `main_light.shadow_enabled = false`, glow off, then
|
||||
`fire_rig.main_light.visible = false`.
|
||||
|
||||
## Pitfalls found while implementing
|
||||
|
||||
1. **Dual-paraboloid shadows do not exist in Compatibility.** Setting
|
||||
`OmniLight3D.SHADOW_DUAL_PARABOLOID` logs `Dual paraboloid shadow mode not supported in
|
||||
the Compatibility renderer` and the light loses its shadows. Only `SHADOW_CUBE`
|
||||
works, even though the class reference describes the other mode as faster. Advice
|
||||
found online ("try dual paraboloid first") is wrong for this renderer.
|
||||
2. Keep `omni_range` constant; animating it makes the light edge visibly expand.
|
||||
3. Never switch the light off to flicker: the lit area visibly pops afterwards.
|
||||
4. Keep fire particles unshaded and additive; the light must only light the room.
|
||||
5. Raise the glow threshold before trusting it: at the default 1.0 the amber HUD panels
|
||||
bloom.
|
||||
6. When taking verification screenshots, stop wave spawning first: a zombie that reached
|
||||
the camera looks like a rendering bug but is just a silhouette in front of the lens.
|
||||
|
||||
## Verification
|
||||
|
||||
```sh
|
||||
"$G" --headless --path . --script res://tools/test_fire_light.gd
|
||||
"$G" --display-driver x11 --rendering-method gl_compatibility --path . --script res://tools/shot_fire.gd
|
||||
```
|
||||
|
||||
`test_fire_light.gd` simulates 60 s at 60 Hz on the pure noise signal and checks: energy
|
||||
stays inside 1.9-2.8 (measured 1.96-2.76), deviation above 0.05 (0.135, so the fire is
|
||||
alive), maximum per-frame step below 0.05 (0.036, so no strobing), the three lights move
|
||||
together, the range stays fixed, bounce and core have no shadows and no specular, the
|
||||
bounce is a child at +40% range, only the main light casts shadows, the sway stays inside
|
||||
±4 cm (measured 3.5 cm) while actually moving, the flame height varies, embers exist,
|
||||
and two rigs with the same seed produce identical signals. `test_office_atmosphere.gd`
|
||||
was updated to the new light budget. All eleven suites pass.
|
||||
|
||||
Images `/tmp/zombie-fire-{basso,alto,alto-scarto,insieme}.png` compare the dim and bright
|
||||
phases, the same brightness with a different sway phase, and a wide view: the wall and
|
||||
ceiling illumination, the floor shadow and the flame halo all change together, and the
|
||||
HUD stays crisp.
|
||||
Reference in New Issue
Block a user