Ambient light

From Valve Developer Community
Jump to navigation Jump to search

Ambient light, better known as indirect diffuse light, is light that has bounced off one or more surfaces before reaching and illuminating a model. It originates exclusively from a map's static light sources and is calculated by VRAD during map compilation.

Ambient light is typically lesser and more nuanced than direct light. It primarily ensures that models are illuminated in locations devoid of any direct light, such as corners, dark rooms, and places where the sun is occluded. It also fills in the pinch spots and tight crevices on a model that are not reached by direct light. Even in the presence of strong, overhead direct lights, ambient light reflected from the ground is often the only significant source of illumination for the underside and interior of models.

In outdoor environments, ambient light further adds realism by contributing the subtle color changes across a model's surface that are expected in large, open areas with many varied light sources (such as an daytime sky). In indoor environments, especially those with few light sources and many occluders, ambient light may be the dominant or exclusive source of illumination for a model.

Creating ambient light

Stage 1: Hammer

The map creator must place one or more static light sources in the map. These are the only light sources which contribute to VRAD's simulation and are thus the only way to add ambient light to a map.

Stage 2: VRAD and Ambient Cubes

Ambient cubes within the red-outlined visleaf. Note how the "left" side of several cubes is brighter than the front - this is the result of the bright sunlight reaching the cubes after reflecting off the angled wall.

VRAD runs an extensive radiosity simulation, calculating the emission, reflection, and absorption of light within the entire sealed volume of the world. Light originates from static map lights, bounces off world surfaces, and is attenuated per the material parameters of those surfaces and the lights.rad file. When the simulation is complete, it is possible to sample any arbitrary position in the world's volume and obtain the total amount of incoming light from all directions at that position. This quantity, minus any direct light, now becomes the ambient light at that position.

VRAD's work is exceptionally slow and is not feasible for realtime rendering. Additionally, storing the entire result of the simulation in the bsp would create prohibitively massive map files. To optimize both ingame rendering and storage, VRAD only stores a tiny number of ambient light samples in the map file. Each visleaf receives only a sparse handful of samples, known as "ambient cubes", which are scattered throughout the visleaf. VRAD attempts to evenly balance the distribution of ambient cubes among differently sized visleafs, but smaller visleaves will typically have a denser ambient cubes assortment than larger ones, and less-than-perfectly-cubic visleafs often have questionable cube placement. Each ambient cube only stores the incoming ambient light from the 6 cardinal directions that align with the normals of the cube's 6 faces. When ingame, the command r_visambient 1 will reveal the ambient cubes in the current visleaf, which can also be visualized with mat_leafvis 1.

Stage 3: In the Engine

Forward rendering in Source. Note the "Ambient Cube" pass, wherein Alyx is subtly illuminated by gray-blue light bouncing off the gray-blue walls.

With VRAD's enormous simulation reduced to just a few thousand ambient cubes, approximate realtime ambient lighting on models is now feasible. The entirety of the model receives ambient light from a single sample, taken at the model's $illumposition. There is no per-vertex or per-pixel ambient lighting technique.

Tip.pngTip:The role of $illumposition is sometimes why models might appear much darker or brighter than they should when against a wall or near an elevation change in the ground. The $illumposition is now proximal to an ambient cube that is on the other side of the wall or ground.
  • Dynamically-lit objects, including players & dynamic props, have their illumination by ambient light calculated every frame, since these objects move around. The lighting technique - selected by the r_radiosity convar - uses either the map's ambient cubes for a cheap approximation (modes 1, 3, and 4) or a much more expensive but still very limited raytracing routine (mode 2). The raytracing routine often produces the most accurate lighting, since it is the only mode which incorporates the actual color and exposure of local surfaces, notably the ground and the skybox.
  • Statically-lit objects, such as static props placed in Hammer, have their illumination by ambient light calculated only once, when the map is loading. Since their lightning is rendered offline and only once, most of the r_radiosity choices (modes 2, 3, and 4) use the aforementioned raytracing routine for static props, instead of the cheaper approximation. Changing the convars which control ambient lighting requires restarting the map to re-render the lighting for these static props.

See the r_radiosity convar below for more information on the various ambient light contribution modes.

Todo: Add a section explaining the concept, purpose, and application of the ambient boost mechanic.

Console commands

mat_leafvis 1; r_visambient 1
These two commands together enable the display the ambient lighting sample points in the active leaf.
r_flashlightambient <int>
Seems like it would be used for making ambient light originating from projected textures or for influencing the color of the flashlight based on ambient light. Does not do anything in Half-Life 2.
r_lightcache_numambientsamples <int>
Cheat. The number of random directions to fire rays when computing ambient lighting. Default 162. Requires map restart.
Confirm:This only affects static props.
r_ambientlightingonly <bool>
Cheat. Light models with only ambient lighting; requires map restart.
r_radiosity <int>
Cheat. Selects the extent and technique used to calculate illumination by ambient light on entities. Mode 2 typically produces the most accurate lighting.
  • 0 = No ambient light
VRAD-compiled lights only contribute direct light to entities. Without any ambient light, entities may become pitch black in areas untouched by direct light.
  • 1 = Cheap ambient light on all entities
All entities receive a very rough approximation of ambient light, using only the light which VRAD calculated and stored along the nearest ambient cube's 6 axis-aligned normals. When there is significant disparity in the amount of light exposure at the model's actual location versus at the nearest ambient cube, the model will be incorrectly lit, appearing lighter, darker, or differently colored compared to its surroundings.
  • 2 = Raytraced ambient light all entities
All entities receive a greatly improved approximation of ambient light from their own position. Ambient light is calculated in real time along r_lightcache_numambientsamples directions originating from the entity's $illumposition, arranged in a sphere for equal coverage. Each sample contributes reflected light from the surface it strikes - such as walls, the ground, and the skybox - making this the only mode with the potential to accurately illuminate a model when the nearest ambient cube is too disparate. Generally, this mode produces the most accurate final model lighting, though it is not without shortcomings. When applied to dynamic entities, the reflected light often comes from surfaces that introduce erroneous additional light, frequently causing the entity to appear anywhere from slightly to noticeably brighter than their surroundings, particularly when indoors. With the default value of 162 for r_lightcache_numambientsamples, the subtle light details captured by this mode are best seen on smooth surfaces of solid color.
  • 3 = Raytraced ambient light on static props, cheap ambient light on all other entities
Static props receive ambient light per r_radiosity 2, while everything else receives ambient light per r_radiosity 1. This is the default value for most Source games.
  • 4 = Raytraced ambient light on static props, reconstructed ambient light on all other entities
Static props receive ambient light per r_radiosity 2, while everything else receives ambient light from an alternate ambient cube algorithm. Instead of prescribing the nearest ambient cube's exposure to the entity, however far away that cube may be, this mode attempts to reconstruct VRAD's high-resolution ambient light calculation at the entity's actual location, using only the handful of ambient cubes that VRAD left behind in the visleaf where the entity is currently located. In practice, the end result lands somewhere between r_radiosity 1 and r_radiosity 2 in all regards. Since there are often very few ambient cubes in any visleaf compared to 162 r_lightcache_numambientsamples, this mode is often faster than r_radiosity 2, while still producing a more accurate result than r_radiosity 1, especially when the entity is very close to an ambient cube. It has significant drawbacks, however. Most notably, the small number of available ambient cubes results in a reconstructed term that is highly diffuse throughout the visleaf, often causing entities to receive a near constant amount of ambient light in the vicinity of any given ambient cube. As a result, the dark spots, tight crevices, and underside of the model tend to receive more light than they should, reducing the contrast of the model's curvature and making it appear flatter. Additionally, the spareness of the ambient cubes and their great variation in location will fail to accurately represent the light received from bounces off brushes extremely local to the entity, such as walls and the ground. Finally, this mode fails to account for the map's lightstyle, causing entities to appear much brighter than they should when lightstyle is used to reduce the overall map light.
  • Values below 0 or above 4 are myths and are equivalent to r_radiosity 0.
r_ambientboost <bool>
Boosts ambient term if it is totally swamped by local lights.
r_ambientmin <float>
Threshold above which ambient cube will not boost (i.e. it's already sufficiently bright). Default 0.3.
r_ambientfraction <float>
Cheat. Fraction of direct lighting that ambient cube must be below to trigger boosting. Default 0.1.
r_ambientfactor <float>
Boost ambient cube by no more than this factor. Default 5.