This is the documentation for Enlighten.
High level runtime
This page guides you to use the High Level Runtime API to drive Enlighten runtime radiosity updates.
The update manager object provides a simple interface to drive Enlighten runtime radiosity updates.
The member functions of IUpdateManager are not safe to call from different threads. Choose one thread on which to create the update manager and interact with it only from that thread.
Functions named with Enqueue are not immediately executed, and are instead queued for async execution. There is no guarantee that such an async function has completed unless you call IUpdateManager::FlushCommands to block the current thread until completion of pending commands. Where an IUpdateManager member function takes an argument of pointer type, you the caller must ensure the lifetime of the referenced object is as described in the documentation for the function.
To drive Enlighten radiosity updates you provide information about lights, shadows and materials that matches what you render, and in turn your renderer consumes the Enlighten outputs. To simplify communication between your engine and the high level runtime we recommend to drive the high level runtime from the same thread that drives your renderer.
Output textures
To use the high level runtime you must provide output texture implementations. These are objects which implement an efficient way to write to a texture on the CPU, using your preferred graphics API.
Each output texture object must hold a reference to the texture resource within your engine that it will write to.
Implement the IGpuTexture and IGpuTextureUpdater interfaces. To get started quickly, copy the sample implementation Dx11TextureUpdater. The key functions are:
- IGpuTextureUpdater::GetCpuAccessiblePointer: returns a pointer to texture memory that can be written by Enlighten.
- IGpuTextureUpdater::Update: synchronizes the written data to the GPU, if necessary.
In some cases you can make updates much more efficient if you create a specialized implementation for a particular target hardware or graphics API. For example, some platforms provide texture resources allocated in shared memory which can be written directly by the CPU.
Initial setup
Call MultithreadCpuUpdateManager::Create to create an instance of the high level runtime which supports async updates. Provide UpdateManagerProperties to configure the radiosity computation.
The default properties provide a good balance between quality and cost:
Property | Default |
---|---|
Lightmap output type | directional irradiance |
Lightmap output format | half float (FP16) |
Probe output type | L1 SH |
Probe output format | uncompressed float |
Cubemap output format | half float (FP16) |
We recommend to use these default values at first, but you can customize them later on if you need to.
Enlighten::UpdateManagerProperties properties; Geo::GeoUniqueReleasePtr<Enlighten::IUpdateManager> updateManager = Enlighten::MultithreadCpuUpdateManager::Create(properties);
MultithreadCpuUpdateManager::Create returns null if you have not yet set up a logger for error and warning messages.
Load data
When you load a chunk of the Enlighten runtime data, add it to the high level runtime.
To allocate runtime objects for each system, probe set and cubemap, call IUpdateManager::AllocateSystem, IUpdateManager::AllocateProbeSet or IUpdateManager::AllocateCubeMap. Provide radiosity data and the appropriate output textures for your chosen output type and format. If the system does not have a lightmap, use the IUpdateManager::AllocateSystem overload which does not take a RadSystemCore or any output textures.
Call IUpdateManager::EnqeueueAddSystem, IUpdateManager::EnqueueAddProbeSet or IUpdateManager::EnqueueAddCubeMap with the allocated runtime objects.
Enlighten::IGpuTexture* outputTextures[Enlighten::ENLIGHTEN_NUM_OUTPUT_TEXTURE_TYPES] = { nullptr }; outputTextures[Enlighten::ENLIGHTEN_OUTPUT_IRRADIANCE] = irradianceTexture; outputTextures[Enlighten::ENLIGHTEN_OUTPUT_DIRECTIONAL] = directionalTexture; Enlighten::BaseSystem* object = updateManager->AllocateSystem(radiosityCore, inputWorkspace, directionalVisibility, outputTextures); updateManager->EnqueueAddSystem(object);
If the instances within this system are lit using probes, use the overload of IUpdateManager::AllocateSystem which does not require the radiosity core and output textures.
To configure each object, immediately after adding it, call the property setter functions of BaseSystem, BaseProbeSet or BaseCubeMap with either EnqueueWorkerFunctorCommand or EnqueueSetObjectParameter.
You must not call any member function of these runtime objects directly. If you do this in a Debug build a fatal error is reported.
To enable sky lighting, call the property setters BaseSystem::SetEmissiveEnvironment, BaseProbeSet::SetEmissiveEnvironment or BaseCubeMap::SetEmissiveEnvironment.
If you use C++11, call EnqueueWorkerFunctorCommand with a lambda.
Enlighten::EnqueueWorkerFunctorCommand(updateManager.GetPtr(), [=](Enlighten::IUpdateManagerWorker* worker) { object->SetEmissiveEnvironment(environmentGuid); });
If you do not use C++11, call EnqueueSetObjectParameter.
Enlighten::EnqueueSetObjectParameter(updateManager.GetPtr(), object, &Enlighten::BaseSystem::SetEmissiveEnvironment, environmentGuid);
To provide material colors for each system, call the property setter BaseSystem::SetAlbedoData.
Enlighten::EnqueueWorkerFunctorCommand(updateManager.GetPtr(), [=](Enlighten::IUpdateManagerWorker* worker) { Enlighten::SystemAlbedoData AlbedoData; AlbedoData.m_AlbedoBuffer = albedoBuffer; object->SetAlbedoData(AlbedoData); });
Each frame
If any of the inputs changed since the previous frame, provide the data to Enlighten.
Call IUpdateManager::EnqueueUpdateEmissiveEnvironment to update environment lighting.
The array of environment lighting values you provide must live at least until the async command completes. We recommend to create this array at the same time as the update manager, and to delete it after you release the update manager.
To provide light sources, call IUpdateManager::EnqueueUpdateLight for every light source in the world. Most common light types are already supported, including Spotlight, PointLight, and DirectionalLight. It's easy to construct each light type from your engine's light source data.
Enlighten::DirectionalLight light; light.m_Intensity = intensity; light.m_Direction = direction; updateManager->EnqueueUpdateLight(lightGuid, light);
The intensity property of a light defines the amount of red, green and blue light that arrives at a surface illuminated by the light source.
Both point and spot lights require a falloff table. Use the default falloff table for physically based inverse square falloff.
Geo::GeoUniquePtr<Enlighten::InputLightFalloffTable> falloffTable(GEO_NEW(Enlighten::InputLightFalloffTable));
Any light falloff table you provide must live at least until the update manager is released. We recommend to create this object at the same time as the update manager, and to delete it after you release the update manager.
Enlighten::PointLight light; light.m_Intensity = intensity; light.m_Position = position; light.m_Radius = radius; light.m_CutOff = cutoff; light.m_FalloffTable = falloffTable; updateManager->EnqueueUpdateLight(lightGuid, light);
The update manager automatically detects when a light did not change since the previous call to IUpdateManager::EnqueueUpdateLight and skips unnecessary computations. If you have a large number of light sources that did not change since the last frame, you can skip IUpdateManager::EnqueueUpdateLight to save time.
Finally, call IUpdateManager::Update to poll for new output and start an asynchronous radiosity update.
updateManager->Update()
To ensure queued updates are processed in a timely manner, call IUpdateManager::Update consistently every frame.
Unload data
For each light that was removed from the world, call IUpdateManager::EnqueueRemoveLight.
For each object to be unloaded, call IUpdateManager::EnqueueRemoveSystem, IUpdateManager::EnqueueRemoveProbeSet or IUpdateManager::EnqueueRemoveCubeMap.
The high level runtime calls your implementation of IGpuTexture::Release during the next IUpdateManager::Update after the object is removed. Safely delete the IGpuTexture object within your IGpuTexture::Release implementation.
The radiosity data you provided when adding the system, probe set or cubemap may still be in use until the async remove command completes. To safely delete the radiosity data after the remove command completes, enqueue a custom command.
updateManager->EnqueueRemoveSystem(systemGuid); Enlighten::EnqueueWorkerFunctorCommand(updateManager.GetPtr(), [=](Enlighten::IUpdateManagerWorker* worker) { // delete the radiosity data here });
Do not delete the AlbedoBuffer or EmissiveBuffer object provided to BaseSystem::SetAlbedoData until the async remove command completes.