Since update 0.39, BeamNG uses a new sky and atmosphere pipeline for outdoor lighting. It provides the main lighting context for a level: sun and moon light, atmospheric sky color, directional ambient light, cloud transmittance, fog interaction, stars, the Milky Way, constellations, meteors, and optional sky profiles.
In current levels this is usually built from:
TimeOfDay - level clock, seasonal sun position, observer location, date, and optional profile selection.ScatterSky - atmospheric sky rendering, sky ambient, night-sky rendering, and sky art controls.CloudLayer - cloud cover and cloud transmittance that affect sky lighting.LevelInfo - level-wide fog density, visible distance, environment map, and related level settings.core_celestial - runtime system that reads TimeOfDay and pushes the live sun, moon, stars, meteors, and profile data into ScatterSky.For a normal outdoor level, start simple:
TimeOfDay, one ScatterSky, one CloudLayer, and one LevelInfo.TimeOfDay.time / startTime for the initial local time.latitude, longitude, year, month, day, and utcOffset if the sky should match a real place and date.celestialProfile or level-local celestial.json only when you need a custom atmosphere or extra bodies.LevelInfo.fogDensity / fogAtmosphereHeight, clouds with CloudLayer, and sky look with the atmosphere/profile values.Most levels should not need script-driven bodies, custom curves, or debug overlays.
The authored sky data is the same across graphics presets, but some settings change how much of it is visible or how accurately it contributes to lighting:
When testing level lighting, check the intended quality preset first, then verify that the level still reads well on lower presets.
Starting with update 0.39, the sky pipeline is physically based. The sky is evaluated into lookup textures and directional ambient data, then used by scene lighting, fog, clouds, reflections, and exposure.
TimeOfDay is the source of the level’s clock, place, and date. The celestial runtime reads that data just before rendering and updates ScatterSky with the current sun, moon, star field, meteor shower state, and optional profile atmosphere. ScatterSky no longer needs to listen directly to TimeOfDay; this avoids two systems fighting over the same sky positions.
Default values in this system are calibrated around physically correct brightness. The sun, moon, stars, Milky Way, meteors, atmospheric sky, and related exposure response are intended to start from real-world scale, with artistic controls acting as offsets from that baseline.
For artists and modders, sky lighting is no longer just a flat color:
The daytime sky is generated from an atmosphere model rather than from a painted sky color. Sunlight enters the atmosphere, is scattered by air molecules, aerosols, and absorption layers, and is evaluated into a small set of lookup textures. These lookup textures let the renderer reuse expensive atmosphere integration for the visible sky, sun transmittance, aerial perspective, fog, and ambient lighting.
The current sky pass builds these results in stages:
The main atmosphere controls are:
Single scattering is the direct contribution from sunlight scattering once toward the camera. Multiscattering is the extra contribution from light that bounces through the atmosphere multiple times before reaching the camera. Without multiscattering, skies tend to lose too much energy and can look unnaturally dark in hazy or low-angle lighting. In BeamNG this is handled by the multiscatter LUT and reused by the sky-view and camera-volume passes.
At render time the sky result is not only drawn behind the level. It also drives the outdoor lighting context. Direct sun and moon light come from their current directions, ambient sky lighting is stored as SH9 directional data, clouds can reduce sun and sky transmittance, and fog uses the same atmospheric color basis so it follows the current time of day.
Outdoor ambient lighting is computed separately from the visible sky by sampling the sky into SH9 data. When volumetric clouds are active, those samples can trace through the cloud layer, so cloud coverage can affect both the visible sky and the ambient light received by level objects. The same process also calculates sun transmittance at the current view position for consistent sunlight attenuation.
Shadow maps, PCSS, screen-space shadows, and GTAO+ determine where direct and ambient sky light reaches scene surfaces. See Lighting Techniques for how these methods work together.
The celestial runtime uses TimeOfDay’s local clock, latitude, longitude, date, and UTC offset to place the sun, moon, stars, and meteor showers. This means two levels with the same clock time can still have different skies if they use different locations, dates, or time zones.
The moon is rendered as a lit body. Its position, lit fraction, and light contribution are driven by the astronomical runtime. A full moon has a high moonIllumination value and can contribute night light, while a new moon has little visible lit surface and much less moonlight. The disc shader uses the direction to the sun to place the terminator, with optional earthshine and artistic EV offsets for readability.
Solar eclipses happen when the moon passes close enough to the sun in the sky that the two discs overlap from the level’s observer position. The runtime can search for the next solar eclipse visible from the level’s TimeOfDay latitude and longitude. During rendering, the directional sun brightness is reduced when the moon disc overlaps the sun disc, based on their angular sizes and separation.
This is intentionally a sky and lighting simulation, not a full scientific eclipse renderer. It gives the correct authoring relationship: an eclipse depends on the level’s place, date, time, moon position, and sun position. If those values are changed, the apparent eclipse timing and strength change with them.
TimeOfDay controls the normalized local day time and stores observer information used by the current astronomical sky:
| Field | Meaning |
|---|---|
time / startTime |
Normalized local civil day. 0 is local noon, 0.25 is evening, 0.5 is midnight, and 0.75 is morning. |
play |
Whether time advances automatically. |
dayLength |
Real seconds per full virtual day. |
latitude |
Observer latitude in degrees north. |
longitude |
Observer longitude in degrees east. |
year, month, day |
UTC calendar date for real sky positions. |
utcOffset |
Optional dynamic field for exact civil UTC offset in hours, including DST. |
dstRule |
Optional dynamic field: auto, eu, us, au, or none. |
celestialProfile |
Optional dynamic field selecting a sky profile, such as earth or a direct JSON path. |
Latitude, longitude, date, and UTC offset affect the sun, moon, stars, constellation orientation, and meteor shower schedule. Use them when a level should have a believable real-world sky.
ScatterSky.starAzimuthSign.ScatterSky is still the main sky object, but in the update 0.39 sky path the active controls are the sky LUT atmosphere fields, celestial runtime fields, cloud transmittance, exposure, and debug/customization fields.
Older ScatterSky fields may still appear in saved level files, but this page only covers the current authoring controls.
Only azimuth and elevation are networked fields on ScatterSky. For full changes to other fields, apply the object changes through the editor or reload/update the level object.
Current atmosphere fields include:
| Group | Useful fields |
|---|---|
| Rayleigh | atmoRayleighScattering, atmoRayleighScaleHeight |
| Mie/aerosols | atmoMieScattering, atmoMieExtinction, atmoMieScaleHeight, atmoMiePhaseG |
| Absorption | atmoAbsorption, atmoOzoneLayerWidth, atmoOzoneConst0, atmoOzoneLinear0, atmoOzoneConst1, atmoOzoneLinear1 |
| Planet/solar | atmoPlanetRadius, atmoThickness, atmoSunIrradiance, atmoSunAngularSize, atmoGroundAlbedo |
| Profile look | skyBrightness, exposure |
The celestial runtime adds several customization paths on top of the stock Earth sky. These are mostly for experienced modders, tools, and unusual skies:
| Feature | Options |
|---|---|
| Profile selection | TimeOfDay.celestialProfile, level-local celestial.json, core_celestial.loadProfile(pathOrTable) |
| Runtime driver | core_celestial.setDriver(fn) to override the per-frame sun, bodies, star orientation, and meteor state |
| Extra bodies | Profile bodies[], or script-driven bodies through setCelestialBodies |
| Day/night response curves | setCelestialCurve(skyId, index, points) |
| Celestial picking | pickCelestial for debug/hover tools |
setCelestialBodies bodies support:
| Field | Use |
|---|---|
azimuth, elevation |
Body position in sky coordinates. |
angularSize |
Apparent diameter in degrees. |
illumination |
Lit fraction for phase rendering. |
brightnessEV |
Artistic brightness offset in EV/stops. |
emissive |
Self-lit disc, useful for extra suns or stars. |
toSun |
Direction the body is lit from. |
tint |
RGB color multiplier. |
albedo |
Texture path for the body surface. |
setCelestialCurve has five curve slots. Use these only when a normal sky profile is not enough:
| Index | Curve |
|---|---|
0 |
Night interpolant. |
1 |
Sun disc / Mie response. |
2 |
Directional light brightness. |
3 |
Day/night color blend. |
4 |
Moon/night tint. |
Sky profiles are JSON presets loaded by core_celestial. They are the preferred way to package atmosphere settings for a level or mod.
Profiles are resolved in this order:
TimeOfDay.celestialProfile, if set.celestial.json next to the loaded mission file.If celestialProfile is a bare name, it resolves to /art/skies/profiles/<name>.json. For example, earth loads /art/skies/profiles/earth.json. It can also be a direct path such as /levels/my_level/celestial.json.
The stock profiles are:
| Profile | Path | Use |
|---|---|---|
earth |
/art/skies/profiles/earth.json |
Documents the default Earth-like sky values. Loading it should not visibly change a normal level. |
mars |
/art/skies/profiles/mars.json |
Non-Earth atmosphere preset. Useful as a reference, but start custom level work from Earth unless you intentionally need another planet. |
Minimal Earth-based profile shape:
{
"name": "my_sky",
"atmosphere": {
"planetRadiusKm": 6360,
"atmosphereHeightKm": 100,
"rayleighScattering": [0.005802, 0.013558, 0.0331],
"rayleighScaleHeightKm": 8,
"mieScattering": [0.003996, 0.003996, 0.003996],
"mieExtinction": [0.00444, 0.00444, 0.00444],
"mieScaleHeightKm": 1.2,
"miePhaseG": 0.8,
"ozoneAbsorption": [0.00065, 0.001881, 0.000085],
"groundAlbedo": [0, 0, 0],
"solarIrradiance": [100000, 100000, 100000],
"sunAngularSizeDeg": 0.2525
}
}
Profiles can also define moonAlbedo and extra bodies. Extra bodies may use fixed azimuth / elevation, or simple equatorial motion with ra0, dec, and periodH. Body fields include angularSize, illumination, brightnessEV, emissive, toSun, albedo, and tint.
For script-driven experiments, core_celestial.loadProfile(pathOrTable) replaces the active profile at runtime, and core_celestial.setDriver(fn) can override the per-frame sun, bodies, stars, and meteor state.
Since update 0.39, the procedural astronomical night sky is the normal path for levels.
It renders:
The moon is rendered as a lit, data-driven body and also contributes night lighting. Its position and phase are driven by the astronomical system introduced with the update 0.39 sky path.
Useful author-facing fields:
| Field | Use |
|---|---|
moonEnabled |
Legacy single-moon render toggle. In the current celestial path, the moon is normally pushed as a data-driven body. |
moonAngularSize |
Apparent angular size of the moon disc. |
moonAlbedo |
Near-side moon albedo texture. |
moonBrightnessEV |
Artistic brightness offset in EV/stops. |
moonEarthshine |
Faint glow on the unlit side. |
moonShadowFloor |
Ambient floor for the shadowed side. |
moonLimbDarkening |
Darkening toward the moon edge. |
moonTerminatorSoftness |
Softness of the lit/unlit phase edge. |
moonGlow |
Atmospheric glow around the moon. |
moonFlatten |
Vertical squash for atmospheric or artistic shape. |
moonLightColor |
Color of the night directional light. |
moonIllumination |
Runtime lit fraction, driven by core_celestial. |
moonOrbitNodeRad |
Runtime lunar node used by the moon-path overlay. |
EV fields use stops: 0 means the calibrated value, +1 doubles brightness, and -1 halves it.
The default value of 0 should be treated as the physically calibrated baseline. Increase or decrease EV values only when the level needs an artistic readability adjustment.
The moon disc also uses emissive EV compression at night so high exposure does not immediately clip its details. moonAzimuth, moonElevation, and moonIllumination are runtime-driven and should not be authored as fixed level values.
These controls are artistic offsets on top of calibrated ranges. Use them for readability, not as a substitute for exposure.
| Group | Useful fields |
|---|---|
| Stars | starsEnabled, starCatalog, starSizeScale, starExtinction, starBrightnessEV, starDensity, starHaze, starColorVariation, starNorthOffset, starAzimuthSign, starPulse |
| Runtime stars | starLocalSiderealTime, starLatitude, starVisibility |
| Milky Way | milkyWayEnabled, milkyWayBrightnessEV, milkyWayCore, milkyWayDust, milkyWayWidth, milkyWayWarmth, milkyWayDustScale, milkyWayGalacticX/Y/Z, milkyWayDebug |
| Meteors | meteorsEnabled, meteorBrightnessEV, meteorTrailLength, meteorWidth, meteorSpeed, meteorInject |
| Runtime meteors | meteorRate, meteorShowerFraction, meteorRadiantRA, meteorRadiantDec |
Meteor rate, shower fraction, radiant, star visibility, local sidereal time, latitude, and moon illumination are runtime-driven by the astronomical system. Do not author those as fixed level values unless you are building a specific scripted setup.
Major meteor showers are date-driven. The runtime currently models Quadrantids, Lyrids, Eta Aquariids, Perseids, Orionids, Leonids, and Geminids, plus a low sporadic baseline outside shower peaks.
Default baked data paths are /art/skies/stars/catalog.bin, /art/skies/stars/constellations.bin, and /art/skies/stars/constellation_labels.bin. These are generated by the celestial bake tools and should normally be reused by levels.
Constellation lines and names are part of the update 0.39 night-sky system. They are rendered from baked data and localized names.
Useful fields:
| Field | Use |
|---|---|
constellationsEnabled |
Draw constellation line figures. |
constellationLines |
Baked constellation line data. |
constellationBrightness |
Line brightness. |
constellationThickness |
Line thickness in pixels. |
constellationColor |
Line tint. |
constellationNamesEnabled |
Draw localized constellation labels. |
constellationLabels |
Baked label anchor data. |
constellationNameBrightness |
Label brightness/opacity. |
constellationNameScale |
Label size scale. |
constellationNameColor |
Label tint. |
Constellation names are normally pushed by the runtime from localized translation keys. Do not edit constellationNames manually for normal level authoring.
ScatterSky includes optional celestial overlays for debug use:
| Field | Use |
|---|---|
gridEnabled |
Alt/azimuth grid and cardinal markers. |
gridBrightness |
Grid brightness. |
gridThickness |
Grid line thickness in pixels. |
gridColor |
Grid tint. |
equatorialGridEnabled |
RA/Dec grid, celestial equator, and ecliptic. |
meridianEnabled |
North-zenith-south meridian and zenith marker. |
moonPathEnabled |
Date-driven moon orbital path. |
Keep these disabled for normal gameplay content.
The sky pipeline feeds directional ambient lighting and fog. The update 0.39 volumetric cloud path can also influence ambient lighting and sun transmittance.
The current cloud path is volumetric. Instead of drawing only a flat cloud texture on a dome, the renderer ray-marches through a cloud layer around the camera and integrates cloud density, lighting, and transmittance along the view ray. This lets clouds have depth, soft edges, internal lighting, and changing opacity when viewed from different angles.
Cloud shape is built from several inputs:
Cloud lighting uses the current sun direction, sun irradiance, and sky ambient SH9 data. Direct sunlight is attenuated by cloud density along the path toward the sun, while ambient light fills shadowed cloud areas from the sky. The cloud shader also uses phase functions, powder edge brightening, and an internal multiscattering approximation so dense clouds keep believable brightness instead of becoming flat gray volumes.
Cloud transmittance is used outside the cloud draw pass too. The renderer produces cloud shadow/transmittance data for sunlight and samples clouds while building sky ambient SH9. This is why changing cloud coverage can affect sun strength, ambient lighting in shadows, fog consistency, and lens flare/cloud occlusion, not only the visible cloud shapes.
Important relationships:
CloudLayer.coverage, windSpeed, windDirection, and altitudeKm feed the current volumetric cloud path.coverage changes the large-scale cloud amount. Higher values create more filled weather-map regions and also increase the thin cirrus contribution.windSpeed and windDirection move the cloud noise/weather pattern over time.altitudeKm sets the base altitude of the main volumetric cloud layer.CloudLayer fields may still exist in saved data, but they are not useful controls for the current volumetric cloud path.LevelInfo still controls fog density, atmosphere height, and visible distance.Check the sky with several cloud quality settings, times of day, and weather states before finalizing lighting.
TimeOfDay and one primary ScatterSky for normal outdoor levels.latitude, longitude, date, and UTC offset when the real sky should matter.celestialProfile or level-local celestial.json for custom atmosphere presets.Was this article helpful?