bUsePhotonMapping

bUsePhotonMapping

#Overview

name: bUsePhotonMapping

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 19 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of bUsePhotonMapping is to control whether photon mapping is used in the lighting calculations within Unreal Engine’s Lightmass system. Photon mapping is a global illumination technique that simulates the way light interacts with surfaces in a scene.

The bUsePhotonMapping variable is primarily used in the Lightmass subsystem, which is responsible for pre-computing static lighting in Unreal Engine. It’s part of the engine’s offline lighting calculation tools rather than the runtime rendering system.

The value of this variable is typically set in the Lightmass configuration files or through the engine’s lighting settings. It interacts with other lighting-related variables, such as bUseFinalGathering and bUseIrradiancePhotons, to determine the specific lighting calculation techniques used.

Developers should be aware that enabling photon mapping can significantly increase lighting calculation times but can also lead to more accurate global illumination results. It’s particularly useful for scenes with complex indirect lighting.

Best practices when using this variable include:

  1. Only enable it when higher quality indirect lighting is required, as it can substantially increase build times.
  2. Use it in conjunction with final gathering (bUseFinalGathering) for the best quality results.
  3. Adjust related settings like the number of photons and gathering samples to balance quality and performance.
  4. Consider using it selectively in areas of the scene where indirect lighting is most important.

It’s important to note that while photon mapping can produce high-quality results, it may not always be necessary for all projects, especially those with simpler lighting requirements or stricter build time constraints.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseLightmass.ini:166, section: [DevOptions.PhotonMapping]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:2345

Scope (from outer to inner):

file
function     void FLightmassExporter::WriteSceneSettings

Source code excerpt:

	}
	{
		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.PhotonMapping"), TEXT("bUsePhotonMapping"), bConfigBool, GLightmassIni));
		Scene.PhotonMappingSettings.bUsePhotonMapping = bConfigBool;
		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.PhotonMapping"), TEXT("bUseFinalGathering"), bConfigBool, GLightmassIni));
		Scene.PhotonMappingSettings.bUseFinalGathering = bConfigBool;
		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.PhotonMapping"), TEXT("bUsePhotonDirectLightingInFinalGather"), bConfigBool, GLightmassIni));
		Scene.PhotonMappingSettings.bUsePhotonDirectLightingInFinalGather = bConfigBool;
		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.PhotonMapping"), TEXT("bVisualizeCachedApproximateDirectLighting"), bConfigBool, GLightmassIni));
		Scene.PhotonMappingSettings.bVisualizeCachedApproximateDirectLighting = bConfigBool;

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:27

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::GatherVolumeImportancePhotonDirections

Source code excerpt:

	bool bDebugThisSample) const
{
	if (GeneralSettings.NumIndirectLightingBounces > 0 && PhotonMappingSettings.bUsePhotonMapping && PhotonMappingSettings.bUsePhotonSegmentsForVolumeLighting)
	{
		TArray<FPhotonSegmentElement> FoundPhotonSegments;
		// Gather nearby first bounce photons, which give an estimate of the first bounce incident radiance function,
		// Which we can use to importance sample the real first bounce incident radiance function.
		// See the "Extended Photon Map Implementation" paper.

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:437

Scope (from outer to inner):

file
function     FLinearColor FStaticLightingSystem::FinalGatherSample

Source code excerpt:

						GatherClassification,
						MappingContext, 
						bDebugThisTexel && (!PhotonMappingSettings.bUsePhotonMapping || !PhotonMappingSettings.bVisualizePhotonImportanceSamples));

					checkSlow(FLinearColorUtils::AreFloatsValid(PathVertexOutgoingRadiance));
					Lighting += PathVertexOutgoingRadiance;

#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
					if (PathVertexOutgoingRadiance.R > DELTA || PathVertexOutgoingRadiance.G > DELTA || PathVertexOutgoingRadiance.B > DELTA)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:485

Scope (from outer to inner):

file
namespace    Lightmass
function     FLinearColor FStaticLightingSystem::FinalGatherSample

Source code excerpt:

	if (bDebugThisTexel
		&& GeneralSettings.ViewSingleBounceNumber == BounceNumber
		&& (!PhotonMappingSettings.bUsePhotonMapping || !PhotonMappingSettings.bVisualizePhotonImportanceSamples))
	{
		FDebugStaticLightingRay DebugRay(PathRay.Start, PathRay.End, RayIntersection.bIntersects, bPositiveSample != 0);
		if (RayIntersection.bIntersects)
		{
			DebugRay.End = RayIntersection.IntersectionVertex.WorldPosition;
		}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:1461

Scope (from outer to inner):

file
namespace    Lightmass
function     FFinalGatherSample FStaticLightingSystem::CachePointIncomingRadiance

Source code excerpt:

		// If final gathering is disabled, all indirect lighting will be estimated using photon mapping.
		// This is really only useful for debugging since it requires an excessive number of indirect photons to get indirect shadows for the first bounce.
		if (PhotonMappingSettings.bUsePhotonMapping 
			&& GeneralSettings.NumIndirectLightingBounces > 0
			&& !PhotonMappingSettings.bUseFinalGathering)
		{
			// Use irradiance photons for indirect lighting
			if (PhotonMappingSettings.bUseIrradiancePhotons)
			{

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:1547

Scope (from outer to inner):

file
namespace    Lightmass
function     FFinalGatherSample FStaticLightingSystem::CachePointIncomingRadiance

Source code excerpt:

			if (GeneralSettings.NumIndirectLightingBounces > 0)
			{
				if (PhotonMappingSettings.bUsePhotonMapping)
				{
					LIGHTINGSTAT(FScopedRDTSCTimer PhotonGatherTimer(MappingContext.Stats.ImportancePhotonGatherTime));
					TArray<FPhoton> FoundPhotons;
					// Gather nearby first bounce photons, which give an estimate of the first bounce incident radiance function,
					// Which we can use to importance sample the real first bounce incident radiance function.
					// See the "Extended Photon Map Implementation" paper.

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/FinalGather.cpp:1656

Scope: file

Source code excerpt:

						if (bDebugThisTexel 
							&& GeneralSettings.ViewSingleBounceNumber == BounceNumber
							&& (!PhotonMappingSettings.bUsePhotonMapping || !PhotonMappingSettings.bVisualizePhotonImportanceSamples))
						{
							FDebugStaticLightingRay DebugRay(TexelRay.Start, TexelRay.End, Intersection.bIntersects, false);
							if (Intersection.bIntersects)
							{
								DebugRay.End = Intersection.IntersectionVertex.WorldPosition;
							}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:610

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::MultithreadProcess

Source code excerpt:

	CacheSamples();

	if (PhotonMappingSettings.bUsePhotonMapping)
	{		
		// Build photon maps
		EmitPhotons();
	}

	if (ImportanceTracingSettings.bUseRadiositySolverForSkylightMultibounce)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:845

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FStaticLightingSystem::GetNumPhotonImportanceHemisphereSamples

Source code excerpt:

int32 FStaticLightingSystem::GetNumPhotonImportanceHemisphereSamples() const
{
	return PhotonMappingSettings.bUsePhotonMapping ? 
		FMath::TruncToInt(ImportanceTracingSettings.NumHemisphereSamples * PhotonMappingSettings.FinalGatherImportanceSampleFraction) : 0;
}

FBoxSphereBounds3f FStaticLightingSystem::GetImportanceBounds(bool bClampToScene) const
{
	FBoxSphereBounds3f ImportanceBounds = Scene.GetImportanceBounds();

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:918

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings

Source code excerpt:

	}

	if (InScene.PhotonMappingSettings.bUsePhotonMapping && !InScene.PhotonMappingSettings.bUseFinalGathering)
	{
		// Irradiance caching currently only supported with final gathering
		InScene.IrradianceCachingSettings.bAllowIrradianceCaching = false;
	}

	InScene.PhotonMappingSettings.ConeFilterConstant = FMath::Max(InScene.PhotonMappingSettings.ConeFilterConstant, 1.0f);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:983

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::DumpStats

Source code excerpt:

	}

	if (PhotonMappingSettings.bUsePhotonMapping)
	{
		SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    Emit Direct Photons\n"), 100.0f * Stats.EmitDirectPhotonsTime / TotalStaticLightingTime, Stats.EmitDirectPhotonsTime);
		SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    Cache Indirect Photon Paths\n"), 100.0f * Stats.CachingIndirectPhotonPathsTime / TotalStaticLightingTime, Stats.CachingIndirectPhotonPathsTime);
		SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    Emit Indirect Photons\n"), 100.0f * Stats.EmitIndirectPhotonsTime / TotalStaticLightingTime, Stats.EmitIndirectPhotonsTime);
		if (PhotonMappingSettings.bUseIrradiancePhotons)
		{

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:1011

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::DumpStats

Source code excerpt:

	LogSolverMessage(SolverStats);
	SolverStats = TEXT("");
	if (PhotonMappingSettings.bUsePhotonMapping)
	{
		if (Stats.EmitDirectPhotonsTime / TotalStaticLightingTime > .02) 
		{
			SolverStats += FString::Printf( TEXT("Total Direct Photon Emitting thread seconds: %.1f\n"), Stats.EmitDirectPhotonsThreadTime);
			SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    Sampling Lights\n"), 100.0f * Stats.DirectPhotonsLightSamplingThreadTime / Stats.EmitDirectPhotonsThreadTime, Stats.DirectPhotonsLightSamplingThreadTime);
			SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    Custom attenuation\n"), 100.0f * Stats.DirectCustomAttenuationThreadTime / Stats.EmitDirectPhotonsThreadTime, Stats.DirectCustomAttenuationThreadTime);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:1150

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::DumpStats

Source code excerpt:

	//@todo - rdtsc is also not dependable if the OS changes which processor the thread gets executed on.  
	// Use SetThreadAffinityMask to prevent this case.
	if (PhotonMappingSettings.bUsePhotonMapping)
	{
		SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    ImportancePhotonGatherTime\n"), 100.0f * Stats.ImportancePhotonGatherTime / IndirectLightingCacheTaskThreadTime, Stats.ImportancePhotonGatherTime);
		SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    CalculateImportanceSampleTime\n"), 100.0f * Stats.CalculateImportanceSampleTime / IndirectLightingCacheTaskThreadTime, Stats.CalculateImportanceSampleTime);
	}
	SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    FirstBounceRayTraceTime for %.3f million rays\n"), 100.0f * Stats.FirstBounceRayTraceTime / IndirectLightingCacheTaskThreadTime, Stats.FirstBounceRayTraceTime, Stats.NumFirstBounceRaysTraced / 1000000.0f);
	SolverStats += FString::Printf( TEXT("%4.1f%%%8.1fs    CalculateExitantRadiance\n"), 100.0f * Stats.CalculateExitantRadianceTime / IndirectLightingCacheTaskThreadTime, Stats.CalculateExitantRadianceTime);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:1200

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::DumpStats

Source code excerpt:

	LogSolverMessage(SolverStats);
	SolverStats = TEXT("");
	if (PhotonMappingSettings.bUsePhotonMapping)
	{
		const float FirstPassEmittedPhotonEfficiency = 100.0f * FMath::Max(Stats.NumDirectPhotonsGathered, NumIndirectPhotonPaths) / Stats.NumFirstPassPhotonsEmitted;
		SolverStats += FString::Printf( TEXT("%.3f million first pass Photons Emitted (out of %.3f million requested) to deposit %.3f million Direct Photons and %u Indirect Photon Paths, efficiency of %.2f%%\n"), Stats.NumFirstPassPhotonsEmitted / 1000000.0f, Stats.NumFirstPassPhotonsRequested / 1000000.0f, Stats.NumDirectPhotonsGathered / 1000000.0f, NumIndirectPhotonPaths, FirstPassEmittedPhotonEfficiency);
		const float SecondPassEmittedPhotonEfficiency = 100.0f * Stats.NumIndirectPhotonsGathered / Stats.NumSecondPassPhotonsEmitted;
		SolverStats += FString::Printf( TEXT("%.3f million second pass Photons Emitted (out of %.3f million requested) to deposit %.3f million Indirect Photons, efficiency of %.2f%%\n"), Stats.NumSecondPassPhotonsEmitted / 1000000.0f, Stats.NumSecondPassPhotonsRequested / 1000000.0f, Stats.NumIndirectPhotonsGathered / 1000000.0f, SecondPassEmittedPhotonEfficiency);
		SolverStats += FString::Printf( TEXT("%.3f million Photon Gathers, %.3f million Irradiance Photon Gathers\n"), Stats.NumPhotonGathers / 1000000.0f, Stats.NumIrradiancePhotonMapSearches / 1000000.0f);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:1221

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::DumpStats

Source code excerpt:

	if (IrradianceCachingSettings.bAllowIrradianceCaching)
	{
		const int32 NumIrradianceCacheBounces = PhotonMappingSettings.bUsePhotonMapping ? 1 : GeneralSettings.NumIndirectLightingBounces;
		for (int32 BounceIndex = 0; BounceIndex < NumIrradianceCacheBounces; BounceIndex++)
		{
			const FIrradianceCacheStats& CurrentStats = Stats.Cache[BounceIndex];
			if (CurrentStats.NumCacheLookups > 0)
			{
				const float MissRate = 100.0f * CurrentStats.NumRecords / CurrentStats.NumCacheLookups;

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.cpp:1358

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     void FStaticLightingSystem::CacheSamples

Source code excerpt:

	int32 NumUniformHemisphereSamples = 0;
		
	if (PhotonMappingSettings.bUsePhotonMapping)
	{
		const float NumSamplesFloat = 
			ImportanceTracingSettings.NumHemisphereSamples 
			* GeneralSettings.IndirectLightingQuality;

		NumUniformHemisphereSamples = FMath::TruncToInt(NumSamplesFloat);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/SampleVolume.cpp:548

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ProcessVolumeSamplesTask

Source code excerpt:

		if (GeneralSettings.NumIndirectLightingBounces > 0 
			// Calculating incident radiance for volume samples requires final gathering, since photons are only stored on surfaces.
			&& (!PhotonMappingSettings.bUsePhotonMapping || PhotonMappingSettings.bUseFinalGathering))
		{
			const bool bDebugSamples = false;
			float BackfacingHitsFraction = 0.0f;
			float Unused = 0.0f;

			// Sample radius stores the interpolation radius, but CalculateVolumeSampleIncidentRadiance will use this to push out final gather rays (ignore geometry inside the radius)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:775

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ProcessTextureMapping

Source code excerpt:

	{
		const double DirectLightingStartTime = FPlatformTime::Seconds();
		const bool bCalculateDirectLightingFromPhotons = PhotonMappingSettings.bUsePhotonMapping && PhotonMappingSettings.bVisualizeCachedApproximateDirectLighting;
		// Only continue if photon mapping will not be used for direct lighting
		if (!bCalculateDirectLightingFromPhotons)
		{
			// Iterate over each light that is relevant to the direct lighting of the mesh
			for (int32 LightIndex = 0; LightIndex < TextureMapping->Mesh->RelevantLights.Num(); LightIndex++)
			{

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Public/SceneExport.h:563

Scope (from outer to inner):

file
namespace    Lightmass
class        class FPhotonMappingSettings

Source code excerpt:

	 * Photon mapping benefits many parts of the solver so this is mainly for comparing against other methods.
	 */
	bool bUsePhotonMapping;

	/** 
	 * Debugging - whether to estimate the first bounce lighting by tracing rays from the sample point,
	 * Which is called final gathering, or by using the density of nearby first bounce photons.  
	 * Final gathering is slow but gets vastly better results than using first bounce photons.
	 */