This is the documentation for Enlighten.

Input lighting concepts

Low-level runtime

All input lighting related functions and classes are defined in EnlightenInputLighting.h, which also includes all the source required to enable custom light types.

The input lighting is separated into two stages:

  1. Direct input lighting
  2. Indirect input lighting

The classes and concepts used are described in the following. More details about providing input lighting can be found in Providing input lighting in the per-frame walkthrough.

Direct input lighting

The direct input lighting stage is executed via the DoDirectinputLighting() function, which takes a DirectInputLightingParameters object as input and produces as output a single IncidentLightingBuffer object. This incident lighting buffer represents the incoming light for the set of lights passed into the function. There is no other information represented in the IncidentLightingBuffer, which means that it can be cached and updated only if the lights in the set have changed.

The DirectInputLighting solver already performs these caching calculations automatically and early outs if there is no further work required. An example use of this feature would be to group lights according the frequency of their updates and then run DoDirectinputLighting() on each group, producing a unique IncidentLightingBuffer for each. These multiple IncidentLightingBuffers can then be used as input into the IndirectInputLighting stage described below.

For more information, see the DoDirectinputLighting() and CalcRequiredScratchSpaceMemory() functions in EnlightenInputLighting.h

Indirect input lighting

The indirect input lighting stage takes as input an array of IncidentLightingBuffers, an AlbedoBuffer, EmissiveBuffer, BounceBuffer, and TransparencyWorkspace, and produces as output a single complete InputLightingBuffer. This is the same object which is used as input into the subsequent radiosity and probe solver stages. The IndirectInputLighting stage also calculates how much the lighting has changed between frames. This data is used for temporal coherence.

For more information, see the DoIndirectInputLighting() function in EnlightenInputLighting.h.

IncidentLightingBuffer class

The IncidentLightingBuffer needs to be allocated by the user. For more information, see the CalcIncidentLightingBufferSize() and CreateIncidentLightingBuffer() functions in EnlightenLights.h.

Input workspace

An input workspace is the runtime data storage for the input sample points (dusters) used in the radiosity calculations.

Input lighting buffer

An input lighting buffer stores the current light value for each cluster in the system, as well as data structures to support cross-cluster bounces. This is the mechanism used to pass the input lighting data to the main Enlighten solving functions. The amount of memory required for a lighting buffer can be obtained via Enlighten::CalcInputLightingBufferSize.

Using an input workspace to light input sample points

An input workspace is constructed during the precompute, and contains everything necessary to perform basic lighting functions on the input sample points. It does not have the ability to compute detailed visibility for input sample points. If this is required it must be supplied by the user. See 'Visibility and input workspaces' below.

There are two ways for an application to light the input sample points:

  • Use the raw data (positions, normals, and material info) to light the sample points, typically by rendering to a texture on the GPU. As noted above, this lighting can be done with simplified shaders; only the diffuse surface albedo colour is important. When the array of lit values has been generated it must be passed to the input workspace.
  • Use the lighting functionality built into the input workspace to light the sample points. The light types available are point lights, spot lights and directional lights. Optionally, per-sample visibility can be passed separately to the input workspace and used to modulate any of the lighting functions.
Visibility and input workspaces

The input workspace lighting functions are able to apply per-sample visibility to any light. Each light has an optional storage block for visibility, with one bit per input sample point ('duster'). If this is passed in the visibilityData parameter, the lighting function will read the appropriate bit of visibility data and add the lighting contribution for that sample point only if its visibility is 1.

However, this array of visibility data must be generated and supplied to the input workspace separately:

  • One possible method is to compute the visibility data on the GPU using a shadow-map look-up. The advantage of this split GPU-CPU solution (visibility on GPU, lighting on CPU via the input workspace) is that the visibility can be fully dynamic, but the GPU only needs to know the positions of the duster points, and can leave the rest of the lighting computation to the CPU. The visibility and lighting do not even need to update at the same rate; the visibility will be tied to the GPU frame rate while the lighting can run at the radiosity frame rate, which may not be the same.
  • A second possibility, for directional lights, is to use precomputed visibility. The visibility data produced by the Enlighten precompute is precomputed visibility for directional lights and static geometry. It can be supplied with a call to Enlighten::SetDirectionalVisibility().

Caching the results of lighting computations

As noted above, IncidentLightingBuffers store lighting values only and can thus be created once for a static group of lights, for example at level load time.

Input lighting buffers are likewise under user control and can be re-used as well. Cached values can be added to an input workspace using Enlighten::AddCachedValuesToInputWorkspace. Note that this only makes sense if not only the input lighting is constant but also the system itself, that is, having static albedo and emissive lighting. As such you normally would use static incident light buffers and not static input lighting buffers in Enlighten3.

Custom light types

You can add new, custom light types, for which Enlighten has no built-in implementations. To learn how to do this, see EnlightenCustomerLights.h. You can implement your own light class following the template and instructions provided.

Custom incident lighting buffers

You might choose to offload some of the input lighting calculation to the GPU. While we don't yet provide a GPU implementation of the DoDirectInputLighting() function, we do provide some facility for users to generate an IncidentLightingBuffer from shaded sample points. For more information, see the AddDusterValuesToInputWorkspace function.