Skip to main content

Volumetric Fog

CameraFrame.volumetricFog renders height fog illuminated by scene lights. Directional lights produce atmospheric haze and sun shafts; omni and spot lights create illuminated volumes and beams. The effect samples shadows along view rays, so objects can block the light within the fog.

Setup

Use a perspective camera. Assign a directional light component to volumetricFog.light, or enable local omni or spot lights. Enabling fog without either source leaves the effect inactive. Assigning a non-directional light to light disables the effect; use the local-light settings for omni and spot lights.

The following example assumes cameraEntity has a camera component and sunEntity has a directional light component:

const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera);
cameraFrame.rendering.toneMapping = pc.TONEMAP_NEUTRAL;
cameraFrame.taa.enabled = true;

sunEntity.light.castShadows = true;
cameraFrame.volumetricFog.enabled = true;
cameraFrame.volumetricFog.light = sunEntity.light;
cameraFrame.volumetricFog.density = 0.01;
cameraFrame.volumetricFog.heightBase = 0;
cameraFrame.volumetricFog.heightFalloff = 0.05;
cameraFrame.update();

Choose the height and density to suit your scene's world units. To see shafts, place shadow-casting geometry between the light and the fog, and set the light's shadow distance and resolution to cover the area of interest. Fog is composed before TAA, so enabling TAA smooths the raymarch noise over time. Call cameraFrame.update() after changing settings.

Omni and Spot Lights

Local lights require clustered lighting, which is enabled by default. Enable the light types you want to contribute:

cameraFrame.volumetricFog.localSpotLights = true;
cameraFrame.volumetricFog.localOmniLights = true;
cameraFrame.volumetricFog.localIntensity = 1;
cameraFrame.volumetricFog.localSteps = 12;
cameraFrame.update();

// Adjust one spot light's contribution independently of its surface lighting.
spotEntity.light.volumetricScattering = 2;

LightComponent.volumetricScattering defaults to 1 and applies to omni and spot lights. Set it to 0 to exclude a light from the fog. localIntensity scales all local-light scattering, while intensity controls the directional-light contribution. Narrow beams may need a higher localIntensity because they cross only a short section of each view ray.

Local lights sample the clustered shadow and cookie atlases. Enable shadow casting on the lights to let geometry block their beams, and use cookies to shape the illumination. Shadow atlas resolution affects the detail visible in the beams.

For a scene lit only by local lights, leave volumetricFog.light as null and enable localSpotLights or localOmniLights. The ambient term still contributes, but ambient alone does not activate the effect.

Appearance

SettingDefaultEffect
density0.01Fog density at and below the base height.
heightBase0World-space height above which density starts to fall off.
heightFalloff0.05Exponential density falloff above the base. Use 0 for uniform density.
tintWhiteColor of the fog's scattering medium.
extinction1Absorption relative to scattering. Lower values keep distant fog and shafts brighter, at the expense of physical accuracy.
anisotropy0.6Forward scattering, from 0 to 0.95. Larger values make fog brighter when looking towards the light.
intensity1Directional-light scattering strength.
localIntensity1Omni and spot light scattering strength.
ambientColorWhiteColor of the ambient light scattered by the fog.
ambientIntensity0.02Ambient contribution that keeps shadowed fog visible.

Quality and Performance

SettingDefaultTradeoff
steps24Main raymarch samples, from 4 to 128. More samples improve quality at a higher cost.
localSteps12Samples inside each local light volume, from 2 to 64. Increase for detailed beams.
scale0.5Fog texture resolution relative to the scene render target, from 0.25 to 1. Lower values reduce pixel work.
maxDistance300Maximum world-space distance covered by the raymarch. Limit it to the visible fog range.

Start with the default half-resolution fog and TAA, then increase samples only where noise or banding remains visible. A longer maxDistance spreads the same samples over more space, so long rays may need more steps.

Local-light cost grows with the number of contributing lights, their screen coverage, and localSteps. Omni lights fill a sphere and often cover more of the screen than spot lights. Keep light ranges tight and disable scattering on lights that do not need to illuminate fog.

Gaussian Splats

Fog and depth of field need Gaussian splats to contribute to scene depth to respect the splats' surfaces. This is disabled by default because it costs an extra full-screen render target. Enable it through GSplatParams.sceneDepthWrite:

if (pc.CameraFrame.isSplatSceneDepthSupported(app.graphicsDevice)) {
app.scene.gsplat.sceneDepthWrite = true;
cameraFrame.rendering.samples = 1;
cameraFrame.update();
}

CameraFrame.isSplatSceneDepthSupported checks device support, not the camera's complete configuration. Splat scene depth also requires:

  • No MSAA on the CameraFrame (rendering.samples = 1); TAA can still be used.
  • A camera that clears its whole render target.
  • No depth prepass required for in-scene depth sampling. In particular, rendering.sceneDepthMap = true and ssao.type = pc.SSAOTYPE_LIGHTING prevent this path. Use pc.SSAOTYPE_COMBINE if SSAO is needed alongside splat scene depth.

Unsupported configurations fall back to depth that excludes splats, and a debug build warns when requested splat depth cannot be produced. If the scene relies on splat depth, disable fog and DoF when it is unavailable, or provide suitable depth geometry. Use the depth debug view to inspect the result.

Some devices use lower-precision scene depth. Keep camera clip distances within approximately 0.000015 to 16384 world units on those devices to preserve depth accuracy.

In the Editor

Add and parse camera-frame.mjs, then attach the registered cameraFrame script to the camera entity as described in CameraFrame in the Editor.

Enable the Volumetric Fog group. Its Light field takes the directional light entity; the engine API takes the light component. Enable Local Spot Lights or Local Omni Lights for local illumination, and use the same appearance and quality controls described above. Enable TAA to smooth the fog. If these attributes are missing from an existing project, update the script asset and parse it again.

Examples

Volumetric Fog
Volumetric Fog Local Lights
Volumetric Fog Shafts