This is the documentation for Enlighten.

Solving for irradiance


Overview

After you have computed and fixed all of the input lighting in the scene (or at the very least, all of the input for the systems that are dependencies of the one you want to solve), the next step is the main radiosity solve task. It consists of the following steps:

1. Prepare structure

First prepare the structure describing the task:

RadIrradianceTask irradianceTask;
irradianceTask.m_CoreSystem = radCore;

2. Prepare list of input workspaces

The solver expects to be passed a list of input lighting buffers, one for each of its dependencies, in the correct order. There is a helper function to prepare this list, as shown in the code sample below.

The only input lighting buffer that you absolutely must pass is the one corresponding to the system you wish to solve. All the others are optional; however, no light will be transferred from missing systems.

irradianceTask.m_InputLighting        = GEO_NEW_ARRAY(const InputLightingBuffer*, Enlighten::GetInputWorkspaceListLength(radCore));
Enlighten::PrepareInputLightingList(radCore, const_cast<const InputLightingBuffer**>(&inputLightingBuffer), 1, irradinceTask.m_InputLighting);
// prepare the ordered list of input lighting buffers

irradianceTask.m_Environment                = emissiveEnvironment;
// optional emissive environment

irradianceTask.m_OutputFormat               = Enlighten::ENLIGHTEN_FORMAT_FP16;
// output values as 16-bit floating point vectors

irradianceTask.m_OutputScale                = 1.0f;
// no extra scaling on output

irradianceTask.m_OutputStride               = radCore->m_MetaData.m_OutputWidth;
// lazily assuming stride = width, not always true!

irradianceTask.m_IrradianceOutput           = irradianceOutput;
// must be persistent if using temporal optimisation

irradianceTask.m_PersistentData             = persistentData;
// required for temporal coherence and other data that persists between frames.

irradianceTask.m_TemporalCoherenceThreshold = 0.01f;
// set to -1 to not use temporal optimisation

This code sample requests irradiance output in 16-bit floating point format. However, it is possible to store irradiance data using half the memory with an almost undetectable difference in quality. The trade-off is that you need a few more instructions in the final shader to decode the values. For more information, see LRB Compressed Output Format for Irradiance.

3. Solve for irradiance

Now call the function to solve for irradiance, passing the task structure and the working scratchspace memory:

Geo::u32 timeTakenInMicroseconds, numSolvedPixels;
bool success = SolveIrradianceTask(&irradianceTask, irradianceWorking, timeTakenInMicroseconds, numSolvedPixels);

If the temporal coherence optimisation is used, it requires that all systems are kept in sync so that no changes in lighting are missed. Nevertheless, some systems can be updated less frequently than others by using the Freeze functions in place of a solve. FreezeIrradianceTask performs the minimal housekeeping required to keep track of light changes for the temporal optimisation; no output textures are updated. The input parameters are exactly the same as for SolveIrradianceTask:

Geo::u32 timeTakenInMicroseconds, numSolvedPixels;
bool success = FreezeIrradianceTask(&irradianceTask, irradianceWorking, timeTakenInMicroseconds, numSolvedPixels);

4. Place data in texture

Finally, when the output data has been written, the application places the data in a texture that is visible by the GPU, so that it can be used for final on-screen rendering.