Atlassian uses cookies to improve your browsing experience, perform analytics and research, and conduct advertising. Accept all cookies to indicate that you agree to our use of cookies on your device. Atlassian cookies and tracking notice, (opens new window)

Enlighten SDK 3.10 Documentation
Results will update as you type.
  • Welcome to Enlighten
  • How Enlighten works
  • Artist workflow
  • Install Enlighten
  • Libraries
  • Implementation guide
  • Technical reference
    • Output formats
    • Albedo handling
    • Lightmap lighting models
    • Light probe evaluation
    • Local IBL reflections
    • Light visibility data
    • Custom direct lights
    • Precompute pipeline
    • Low level precompute API
    • Debugging the precompute
    • The low level runtime
      • Low level runtime walkthrough
        • Preparing the runtime
        • Per-frame walkthrough
          • Providing input lighting
          • Solving for irradiance
          • Solving for directional irradiance
          • Solving for probe points
          • Probe interpolation
          • Resampling bounce
        • Multiple systems at runtime
        • Low level cubemap API
      • Input lighting
      • Debug the low level runtime
    • Baked lighting
    • Performance tuning
    • Technical troubleshooting
    • Terrain LOD
    • Probe LOD
    • Lightmap LOD
  • Advanced techniques
  • Tools
  • Enlighten Mobile
  • White papers
  • Third-party licences
  • Release notes
    Calendars

You‘re viewing this with anonymous access, so some content might be blocked.
/
Solving for probe points

    This is the documentation for Enlighten.

    Solving for probe points

    Nov 21, 2019


    Overview

    Beginning in Enlighten version 2.21, there are two alternative methods for solving for light probe values: the original Probe Solver, which uses the RadProbeTask structure; and the newer Entire Probe Set Solver, which uses the EntireProbeSetTask structure.

    Compared to the older Probe Solver, the precomputed data required for the Entire Probe Set Solver is significantly more compressed, working out at 25-30% of the data size for a typical regular grid of probe locations. Unlike the original Probe Solver, which requires different data to be precomputed for L1 and L2 output, the Entire Probe Set Solver can use the same data to solve either L1 or L2 spherical harmonics at runtime. The execution time is also significantly shorter, taking approximately 60% of the time to solve all probes in a set. In addition to this, temporal coherence optimisations are implemented in the Entire Probe Set Solver, so that redundant repeat computations can be skipped when lighting conditions are not changing.

    The trade off is that it is not possible to solve individual probes with the Entire Probe Set Solver.

    Precompute data blocks

    The Enlighten precompute generates data blocks for both the original Probe Solver and the newer Entire Probe Set Solver. These are both members of the RadProbeSetCore class:

    //--------------------------------------------------------------------------------------------------
    /// Constitutes the core precomputed data for a probe set.
    //--------------------------------------------------------------------------------------------------
    class RadProbeSetCore
    {
    public:
    	/// Data describing the probe set, its size, requirements and relations to other systems
    	RadProbeSetMetaData		m_MetaData;
    
    	/// Precomputed data necessary for probe radiosity lighting
    	RadDataBlock			m_ProbeSetPrecomp;
    
    	/// Precomputed data block for the "probe set solver" which solves entire probe sets atomically,
    	/// with optional temporal coherence optimisations.
    	RadDataBlock			m_EntireProbeSetPrecomp;
    

    You need to load only one of these data blocks. If you use the older Probe Solver, load only the m_ProbeSetPrecomp data block. Conversely, if you use the Entire Probe Set Solver, load only the m_EntireProbeSetPrecomp block.

    Providing input lighting data to the probe solver

    The mechanism for passing input to a probe task is the same as for irradiance tasks; you pass an ordered list of input lighting buffer pointers. There are helper functions to prepare this list.

    When using the Entire Probe Set Solver you must use the versions which take a RadDataBlock pointer, and pass the m_EntireProbeSetPrecomp block. (The older version which takes a RadProbeSetCore pointer will return results for the older Probe Solver, or fail if that data block is not loaded.)

    /// Returns the length of the input lighting buffer list expected when solving radiosity using this core data.
    Geo::s32 GEO_CALL GetInputWorkspaceListLength(const Enlighten::RadDataBlock* dataBlock);
    
    /// Returns the GUID of a specific entry in the expected input lighting buffer list.
    Geo::GeoGuid GEO_CALL GetInputWorkspaceGUID(const Enlighten::RadDataBlock* dataBlock, Geo::s32 index);
    
    /// Places the unordered list of lighting buffers into the correct order for the solver.
    bool GEO_CALL PrepareInputLightingList(const Enlighten::RadDataBlock* dataBlock,
                                           const Enlighten::InputLightingBuffer** inputLightingBuffers,
                                           Geo::s32 numLightingBuffers,
                                           const Enlighten::InputLightingBuffer** listILBOut);
    

    Example - Probe Solver

    The following simplified example solves for the first index in the probe set, using the original Probe Solver. In this case, the list of input indices and the list of output pointers are very simple. In a more realistic use case, you would have to decide which indices to compute based on the world position of the dynamic object you wish to relight.

    RadProbeTask probeTask;
    probeTask.m_CoreProbeSet = probeSetCore;
    probeTask.m_InputLighting = GEO_NEW_ARRAY(const InputLighitngBuffer*, Enlighten::GetInputWorkspaceListLength(probeSetCore));
    Enlighten::PrepareInputLightingList(probeSetCore, const_cast<const InputLightingBuffer**>(inputLightingBuffers), numSystems, probeTask.m_InputLighting);
    
    // Make the list of indices we wish to solve for.
    
    s32 indexList[1] = { 0 };
    probeTask.m_NumIndicesToSolve = 1;
    probeTask.m_IndicesToSolve = indexList;
    // List of input indices - only one entry long.
    
    // Reserve space for the output.
    // SolveProbeTaskL1 generates 12 floating-point coefficients per index.
    float shOutput[12];
    float* pShOutput = shOutput;
    probeTask.m_OutputPointers = &pShOutput;
    // List of output pointers - only one entry long.
    
    // Ask Enlighten to compute the light probe data!
    bool solveStatus = Enlighten::SolveProbeTaskL1(&probeTask);
    

    Example - Entire Probe Set Solver

    EntireProbeSetTask probeTask;
    probeTask.m_CoreProbeSet = probeSetCore;
    probeTask.m_InputLighting = GEO_NEW_ARRAY(const InputLighitngBuffer*, Enlighten::GetInputWorkspaceListLength(probeSetCore));
    Enlighten::PrepareInputLightingList(&probeSetCore->m_EntireProbeSetPrecomp, const_cast<const InputLightingBuffer**>(inputLightingBuffers), numSystems, probeTask.m_InputLighting);
    probeTask.m_Environment = <pointer to an input lighting buffer for the emissive environment>
    
    probeTask.m_OutputShOrder = <Enlighten::SH_ORDER_L1 or Enlighten::SH_ORDER_L2>          // choose whether to solve either L1 or L2 SH
    probeTask.m_Output = <pointer to an array of 12 floats per probe for L1 or 27 for L2>	  // supply this pointer for float output
    probeTask.m_U8Output = NULL;								                            // supply this pointer instead for U8 output (only valid for L1)
    probeTask.m_U8OutputScale = 1.0f;							                            // if using U8, use this to change range
    
    // set these parameters to enable temporal coherence optimisations
    probeTask.m_TemporalCoherenceThreshold = 0.01f;
    probeTask.m_TemporalCoherenceBuffer = <a persistent buffer of length probeSetCore->m_MetaData.m_RequiredTemporalCoherenceBufferSize>
    
    // this workspace is always required by SolveEntireProbeSetTask
    void* workspace = <a buffer of length probeSetCore->m_MetaData.m_RequiredWorkspaceSize>
    
    bool solveStatus = Enlighten::SolveEntireProbeSetTask(&probeTask, workspace timeUs, numSolvedProbes);
    

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

    bool solveStatus = Enlighten::FreezeEntireProbeSetTask(&probeTask, workspace timeUs, numSolvedProbes);
    

    Compressed output

    Although the calculation of probe volumes is all done in floating point, it is not always necessary to retain such precision in the final answer. To reduce memory requirements for the L1 solver, it is possible to pass an array of u8 to the probeTask, along with a scale value (similar to the BounceScale). The rest of the setup and function call is unaffected. See Light probe evaluation for more details about the compressed encoding.

    // Reserve space for the output.
    // SolveProbeTaskL1/SolveEntireProbeSetTask generates 12 u8 coefficients per index.
    Geo::u8 shOutput[12];
    Geo::u8* pShOutput = shOutput;
    probeTask.m_U8OutputPointers = &pShOutput;
    probeTask.m_U8OutputScale = 1.0f;
    
    , multiple selections available,
    {"serverDuration": 9, "requestCorrelationId": "a0fc8277cb35472192418d9c70756922"}