INSODIMENSIONStudios
AtmosGuides

Ground Fog

Place terrain-relative mist banks, bake collision height, and tune density, motion, and fade

Ground Fog

Agentic Atmos adds terrain-relative mist inside placed banks using Unreal Engine 5.8 native volumetric fog. Each bank bakes the collision height beneath its footprint into a texture it owns. It does not create ground or add a second fog system.

Terrain-relative ground fog rendered with Agentic Atmos

Rendered with Agentic Atmos in the plugin showcase level, with graphic overlays. Demonstration scenery is not all included. This is a rendered example, not an editor screenshot.

Assigning a day cycle does not place or bake ground-fog banks. A bank needs selected ground collision, a valid saved bake, a resolved renderer, and native Volumetric Fog enabled. An unresolved renderer means off, not a substitute renderer.


Ownership and prerequisites

  • Look preset → Ground Fog modifier. UAtmosLookPreset.GroundFog holds an instanced UAtmosGroundFogModifier. Its typed GroundFog settings describe the medium; RendererTag selects the content. Author here — not in a composed segment field, and not on a bank's transient material instance.
  • Day cycle → Render Profile → renderer. The active UAgenticDayCycle.RenderProfile mounts a UAtmosRenderProfile. Its Renderers map must contain Atmos.Renderer.GroundFog.Native → /AgenticAtmosphere/Core/Renderers/DA_Renderer_GroundFog_Native (a UAtmosGroundFogRenderer). Preserve the profile's other renderer entries.
  • Sky actor. AAtmosSkyActor publishes the composed fog settings and the resolved world wind. The active Fog group must be asserted, its height-fog component visible, and Volumetric Fog enabled.
  • Placed bank. AAtmosGroundFogBank owns spatial inclusion, the explicitly selected ground, exclusions, the saved terrain bake, and its rendering surfaces. It does not create a landscape or guess which floors in your level count as ground.

Add ground fog to an existing level

  1. Work on owned assets. Duplicate the relevant cycle, profile and look if the originals must stay unchanged, then assign your cycle to the existing sky actor. Do not add a second lighting authority just to get fog.
  2. Mount the native renderer in the cycle's profile under Atmos.Renderer.GroundFog.Native. The renderer supplies /AgenticAtmosphere/Materials/GroundFog/M_Atmos_GroundFog and NoiseVolume → /AgenticAtmosphere/Textures/GroundFog/VT_Atmos_GroundFogNoise. Keep its binding rows and TerrainTextureParameter = TerrainHeight; the bank supplies that texture per instance. The production material needs UseTerrainHeight on.
  3. Author the look's Ground Fog slot. Add a Ground Fog modifier, set its renderer tag, enable its typed settings, and start from one of the library looks below. Keep the look's existing lighting, exposure and Fog settings, and make sure native Volumetric Fog is enabled in the composed Fog modifier.
    • A ground-fog-only library look can be a slot's LookPreset over a suitable cycle BaseLook — its absent lighting categories inherit that base.
    • When a slot already has its own lighting look, fork that look and copy the library's Ground Fog settings into a new modifier on the fork. Replacing a full look with a fog-only layer would lose that slot's lighting assertions.
  4. Place an AtmosGroundFogBank actor. Leave rotation zero and scale (1,1,1). Put its XY centre over the intended patch and size it with FootprintSize (full width/length in cm), not actor scale. Set AtmosphereSourceOverride to the intended sky actor for explicit ownership; if unset it uses the registered sky source. bEnabled is a bank-local switch, and optional ActiveAtmosTags restrict it to matching composed atmosphere/segment tags (any match; empty means unrestricted).
  5. Populate GroundActors deliberately. Add unique, loaded actors in this world with registered, query-enabled Landscape or static-mesh collision. Select each Landscape streaming proxy explicitly. Do not select the bank, roofs, props or bridges unless their upper collision surface really is the ground you want.
  6. Set bake bounds. Use a power-of-two TerrainResolution from 8 to 256 (64 default). Set TerrainTraceTopZ above and TerrainTraceBottomZ below the selected collision — these are absolute world-Z centimetres, not offsets from the bank. Keep the range tight. bTerrainTraceComplex selects the trace mode (default true).
  7. Add exclusions before baking. Each enabled TerrainExclusions entry is an axis-aligned world-XY rectangle with a Center and half extents in cm — use them for clear paths or holes around obstacles. The bake conservatively excludes intersecting texels, including rectangles narrower than one texel. TerrainEdgeFadeCm and TerrainExclusionFadeCm soften the baked support boundaries.
  8. Click Bake Terrain in the bank's details panel (BakeTerrain, or bake_terrain() from Python). It runs only on a placed actor in an editor world — never in PIE or a cooked game. Require success, HasValidTerrainBake() == true, IsTerrainBakeStale() == false, and a nonzero BakedValidTexels. Read TerrainBakeStatus for refusal details.
  9. Save the bank's level or external-actor package, plus the edited look, profile and cycle. The bake marks its package dirty; clicking Bake is not a save. Use the sky actor's Refresh Now and the bank's Refresh Ground Fog after repairs. Reopen the saved scene and confirm the bake is still valid before relying on it.
  10. Check a controlled on/off pair. Hold camera, time, lighting, exposure and existing fog constant and toggle only the bank's bEnabled. Let the volumetric temporal history settle. Bank-off does not mean all atmospheric fog is off.

Terrain data and rebaking

Bake Terrain traces the selected collision components downward at texel centres, and the highest accepted hit wins at each XY sample. Unselected props cannot block the trace or become ground. No hit means no fog — there is no fabricated flat plane.

TerrainHeightTexture is a bank-owned texture sub-object saved in the bank's package (including external-actor packages). It encodes:

Channel / metadataMeaning
Rnormalized collision height; decode with BakedHeightMin + R * BakedHeightRange
Gvalidity — missing collision and excluded texels contribute no fog
Bbaked edge/exclusion fade; the native material multiplies G × B
Bank metadataworld-XY origin and size, resolution, valid count, configuration signature, source snapshots

The texture is linear half-float data, nearest-sampled, clamped, without mips, and marked non-streaming. Runtime consumes the saved data; a refresh does not trace, rebake or write assets.

Rebake explicitly after moving a bank, changing footprint / resolution / trace limits, changing the collision mode or baked fades, editing the ground selection or exclusions, or changing the selected terrain itself. Staleness checks cover component transforms, mesh identity and collision revision, instance transforms, Landscape collision revision and the editor heightmap source revision. Confirmed stale terrain reports "Explicitly Bake Terrain again."

Use IsTerrainBakeStale() / HasValidTerrainBake() as the live check, not the last successful status string. Disabled, invalid or stale banks fail closed when refreshed. A failed bake invalidates the bank: read TerrainBakeStatus and the LogAtmosGroundFogTerrain output, fix the selection, bounds or collision, then bake and save again. Density, colour, fade and wind edits do not need a rebake; bank-side baked fades do.


Tune the look

Distances are centimetres unless stated otherwise (100 cm = 1 m). The native material takes extinction in inverse metres — do not divide it by 100 yourself.

ControlDesigner meaning
bEnabled, Strength, ExtinctionPerMetreexplicit on/off and optical density; Strength multiplies extinction once. Zero effective extinction or zero Coverage produces no active medium.
TerrainBaseOffsetCm, LayerDepthCm, VerticalFalloffoffset from baked ground, layer thickness, vertical falloff exponent. Higher falloff holds density closer to the floor; the layer is bounded below as well as above.
Coverage, Breakupcoverage threshold and its softness. Smaller Breakup makes harder banks; larger values soften the threshold. These are 0–1 controls, not percentages.
MacroNoiseScaleCm, DetailNoiseScaleCm, DetailAmount, NoiseContrastworld size of banks and wisps, detail perturbation, contrast around the noise midpoint.
ScatteringTintscattering albedo, clamped 0–1. Not emission, and not a replacement for sun/moon lighting. Directional scattering uses the Fog group's shared distribution.
EdgeFadeCmlive material feather at the bank's volume bounds; distinct from the baked terrain-edge and exclusion fades.
NearFadeStartCm / NearFadeEndCmcamera-distance interval opening from no fog to full fog. Keep end greater than start.
FarFadeStartCm / FarFadeEndCmcamera-distance interval closing from full fog to none. Keep end greater than start.
WindFollow, DriftDirection, DriftSpeedCmPerSecond, AnimationRatefraction of resolved world wind to follow, plus a normalized world-space authored direction × drift speed; animation rate scales the resulting velocity. Zero direction removes authored drift; zero rate freezes it.
SignalChangeEpsilonadvanced relative numeric tolerance for re-deriving signals. Not a quality or frame-rate budget — leave it alone.

A missing modifier does not disable fog. No Ground Fog modifier means "inherit", and a lower layer may still assert fog. To author a genuinely clear interval, use a present modifier with GroundFog.bEnabled = false. The bank's own switch disables only that placed bank.

Advection uses ordinary world GameTime — the same clock as material Time in the level editor, scene captures and PIE — not the authored solar time-of-day clock. Pausing the day cycle is not a request to freeze the mist. Velocity changes preserve accumulated displacement through a rebased epoch, so the fog never pops when the wind changes. Renderer replacements crossfade over CrossfadeSeconds (default 1 s, bounded 0.01–10 s) across at most two bank surfaces.


Start from a library look

Three ground-fog-only UAtmosLookPreset assets live under /AgenticAtmosphere/Looks/Library/GroundFog/. None of them author lighting, exposure or emission — they are starting points, not a promise that the same values suit every camera and landscape.

AssetIntended forExtinction /mCoverageDepth cmDrift cm/sWindFollow
LP_GroundMist_Dawntaller translucent dawn pockets0.080.3250080.15
LP_GroundMist_Backlittaller translucent backlit banks0.060.32600140.25
LP_GroundMist_Moonlittaller translucent moonlit pockets0.070.3645050.10

Dawn's near fade is 600–2400 cm; Backlit and Moonlit use 0–300 cm. All three use far fade 15000–20000 cm, Strength 1, base offset 0, AnimationRate 1 and EdgeFade 800 cm. Macro scales are 3200 / 3400 / 5600 cm, detail scales 220 / 240 / 200 cm, breakup 0.45 / 0.45 / 0.70, detail amount 0.55, vertical falloff 1.0, and noise contrast 1 / 1 / 0.8. Scattering is neutral (1, 1, 1, 1) in all three: native volumetric lighting supplies the sun, moon and skylight colour once, so do not multiply an ambient colour into this lit volume's albedo.

Base offset stays at 0 on purpose — depth increases above the terrain rather than lifting the fog into a floating slab.

The shared renderer owns the content and the binding rows; each look owns its modifier and each bank owns its terrain. Do not rewrite the shared renderer, or tune a bank's transient material instances, to adjust one look.


Requirements and limitations

  • Targets UE 5.8 and its native volumetric-fog path. It needs usable volumetric-fog render/scalability settings, the shipped material and volume texture, and selected collision. A bank uses an engine cube volume host, not a Niagara effect.
  • The noise is generated by Agentic Atmos, deterministic and periodic — a 64³ RGB volume, generated procedurally with no third-party pixel input. The native graph samples the macro channel and the detail channel with two volume taps. It is periodic, not mathematically non-repeating.
  • Landscape: registered, query-enabled heightfield collision is supported, including explicitly selected loaded proxies. Render appearance alone is not collision. Missing or unloaded selected sources make the bake stale, and soft references do not force World Partition cells to load.
  • Static meshes, including Nanite-rendered meshes: supported through UStaticMeshComponent collision, including tracked instanced-mesh transforms. A Nanite flag is not a source of traceable triangles — the collision representation supplied to the trace is what counts, so verify it agrees with the visible ground. Render-only displacement does not become baked height.
  • This is one highest collision height per XY texel, not a 3D terrain volume. Overhangs, caves and stacked walkable floors cannot all be represented at one XY: select ground carefully, partition banks, or exclude the unwanted regions. Finite resolution and conservative safety fades can remove narrow strips — inspect the baked support rather than raising extinction to hide holes.
  • Skeletal, procedural and dynamic-mesh components are not accepted ground types. Runtime deforming or moving terrain is not followed or rebaked automatically, and even supported components go stale when their tracked geometry or transform changes. There is no runtime collision tracing and no generated fallback ground.

Cost

Measured on an RTX 4080 SUPER at 1920×1080, screen percentage 100, with running world time and a moving camera. Five samples per case, toggling only the ground-fog enable flag while native height and volumetric fog stay on. The typical case is one 6300×6300 cm bank; the stress case is four overlapping banks of that size.

Native volumetric gridCaseMedian fog-pass msIncrement over matched OFF
GridPixelSize 8 / GridSizeZ 128off0.250—
8 / 128one bank0.406+0.156 ms
8 / 128four overlapping banks0.424+0.174 ms
16 / 64off0.049—
16 / 64one bank0.084+0.035 ms

Those medians meet the design targets of 0.2 ms typical and 0.5 ms with overlap in this bounded run. Individual samples were noisy (0.31–0.98 ms for one bank at high quality), so treat this as a cost class, not a worst-frame bound or a platform guarantee.

Resource footprint in the same measurement setup: 192 KiB for the shared noise volume texture and 32 KiB per bank for terrain textures. Those are listed footprints, not exclusive VRAM residency — editor and PIE copies coexist, and disabling render work does not unload authored resources.


Next steps

See day cycles for look layering and wind for your content for shared motion. If a bank stays invisible, use troubleshooting or Discord support.