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.