DirectPhotonDensity

DirectPhotonDensity

#Overview

name: DirectPhotonDensity

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 20 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of DirectPhotonDensity is to control the density of direct photons emitted by lights in the scene for photon mapping calculations. It is used in the lighting and rendering system of Unreal Engine 5, specifically for global illumination and photon mapping.

This setting variable is primarily used by the Lightmass subsystem, which is responsible for precomputed lighting in Unreal Engine. It’s part of the photon mapping settings and is used in both the editor (UnrealEd) and the Lightmass standalone application.

The value of this variable is typically set in the Lightmass configuration file (GLightmassIni). It can be modified through the engine’s configuration system or potentially through in-editor settings.

DirectPhotonDensity interacts with several other variables, including:

Developers should be aware that:

  1. This variable significantly impacts the quality and performance of light baking.
  2. Higher values will result in more accurate lighting but will increase baking time and memory usage.
  3. The value is in photons per million surface area units, so it scales with scene size.

Best practices when using this variable include:

  1. Adjust it based on the scale and complexity of your scene.
  2. Balance it with other photon mapping settings for optimal results.
  3. Start with default values and incrementally adjust as needed.
  4. Consider using different values for different quality levels of lightmaps.
  5. Be mindful of the performance impact when increasing this value, especially for large scenes.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseLightmass.ini:191, 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:2381

Scope (from outer to inner):

file
function     void FLightmassExporter::WriteSceneSettings

Source code excerpt:

		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PhotonMapping"), TEXT("NumImportanceSearchPhotons"), Scene.PhotonMappingSettings.NumImportanceSearchPhotons, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("OutsideImportanceVolumeDensityScale"), Scene.PhotonMappingSettings.OutsideImportanceVolumeDensityScale, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("DirectPhotonDensity"), Scene.PhotonMappingSettings.DirectPhotonDensity, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("DirectIrradiancePhotonDensity"), Scene.PhotonMappingSettings.DirectIrradiancePhotonDensity, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("DirectPhotonSearchDistance"), Scene.PhotonMappingSettings.DirectPhotonSearchDistance, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("IndirectPhotonPathDensity"), Scene.PhotonMappingSettings.IndirectPhotonPathDensity, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("IndirectPhotonDensity"), Scene.PhotonMappingSettings.IndirectPhotonDensity, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("IndirectIrradiancePhotonDensity"), Scene.PhotonMappingSettings.IndirectIrradiancePhotonDensity, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PhotonMapping"), TEXT("IndirectPhotonSearchDistance"), Scene.PhotonMappingSettings.IndirectPhotonSearchDistance, GLightmassIni));

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

Scope (from outer to inner):

file
function     void FLightmassExporter::WriteSceneSettings

Source code excerpt:

		float NumDirectPhotonsScale;
		VERIFYLIGHTMASSINI(GConfig->GetFloat(QualitySectionNames[QualityLevel], TEXT("NumDirectPhotonsScale"), NumDirectPhotonsScale, GLightmassIni));
		Scene.PhotonMappingSettings.DirectPhotonDensity = Scene.PhotonMappingSettings.DirectPhotonDensity * NumDirectPhotonsScale;
		Scene.PhotonMappingSettings.DirectIrradiancePhotonDensity = Scene.PhotonMappingSettings.DirectIrradiancePhotonDensity * NumDirectPhotonsScale; 

		float DirectPhotonSearchDistanceScale;
		VERIFYLIGHTMASSINI(GConfig->GetFloat(QualitySectionNames[QualityLevel], TEXT("DirectPhotonSearchDistanceScale"), DirectPhotonSearchDistanceScale, GLightmassIni));
		Scene.PhotonMappingSettings.DirectPhotonSearchDistance = Scene.PhotonMappingSettings.DirectPhotonSearchDistance * DirectPhotonSearchDistanceScale;

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:536

Scope (from outer to inner):

file
namespace    Lightmass
function     void FScene::ApplyStaticLightingScale

Source code excerpt:

	// Scale surface densities in world units
	const float ScaleSquared = SceneConstants.StaticLightingLevelScale * SceneConstants.StaticLightingLevelScale;
	PhotonMappingSettings.DirectPhotonDensity /= ScaleSquared;
	PhotonMappingSettings.DirectIrradiancePhotonDensity /= ScaleSquared;
	PhotonMappingSettings.DirectPhotonSearchDistance *= SceneConstants.StaticLightingLevelScale;
	PhotonMappingSettings.IndirectPhotonPathDensity /= ScaleSquared;
	PhotonMappingSettings.IndirectPhotonDensity /= ScaleSquared;
	PhotonMappingSettings.IndirectIrradiancePhotonDensity /= ScaleSquared;
	PhotonMappingSettings.IndirectPhotonSearchDistance *= SceneConstants.StaticLightingLevelScale;
	*/
}

//----------------------------------------------------------------------------
//	Light base class
//----------------------------------------------------------------------------
void FLight::Import( FLightmassImporter& Importer )
{
	Importer.ImportData( (FLightData*)this );
	Importer.ImportArray( LightTextureProfileData, FLightData::LightProfileTextureDataSize );

	// The read above stomps on CachedLightSurfaceSamples since that memory is padding in FLightData
	FMemory::Memzero(&CachedLightSurfaceSamples, sizeof(CachedLightSurfaceSamples));

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:704

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FDirectionalLight::GetNumDirectPhotons

Source code excerpt:


/** Returns the number of direct photons to gather required by this light. */
int32 FDirectionalLight::GetNumDirectPhotons(float DirectPhotonDensity) const
{
	int32 NumDirectPhotons = 0;
	if (ImportanceBounds.SphereRadius > DELTA)
	{
		// The importance volume is valid, so only gather enough direct photons to meet DirectPhotonDensity inside the importance volume
		const float ImportanceDiskAreaMillions = (float)PI * FMath::Square(ImportanceBounds.SphereRadius) / 1000000.0f;
		checkSlow(SceneBounds.SphereRadius > ImportanceBounds.SphereRadius);
		const float OutsideImportanceDiskAreaMillions = (float)PI * (FMath::Square(SceneBounds.SphereRadius) - FMath::Square(ImportanceBounds.SphereRadius)) / 1000000.0f;
		NumDirectPhotons = FMath::TruncToInt(ImportanceDiskAreaMillions * DirectPhotonDensity + OutsideImportanceDiskAreaMillions * OutsideImportanceVolumeDensity);
	}
	else
	{
		// Gather enough photons to meet DirectPhotonDensity everywhere in the scene
		const float SceneDiskAreaMillions = (float)PI * FMath::Square(SceneBounds.SphereRadius) / 1000000.0f;
		NumDirectPhotons = FMath::TruncToInt(SceneDiskAreaMillions * DirectPhotonDensity);
	}
	return NumDirectPhotons == appTruncErrorCode ? INT_MAX : NumDirectPhotons;
}

/** Generates a direction sample from the light's domain */
void FDirectionalLight::SampleDirection(FLMRandomStream& RandomStream, FLightRay& SampleRay, FVector4f& LightSourceNormal, FVector2f& LightSurfacePosition, float& RayPDF, FLinearColor& Power) const

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:994

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FPointLight::GetNumDirectPhotons

Source code excerpt:


/** Returns the number of direct photons to gather required by this light. */
int32 FPointLight::GetNumDirectPhotons(float DirectPhotonDensity) const
{
	// Gather enough photons to meet DirectPhotonDensity at the influence radius of the point light.
	const float InfluenceSphereSurfaceAreaMillions = 4.0f * (float)PI * FMath::Square(Radius) / 1000000.0f;
	const int32 NumDirectPhotons = FMath::TruncToInt(InfluenceSphereSurfaceAreaMillions * DirectPhotonDensity);
	return NumDirectPhotons == appTruncErrorCode ? INT_MAX : NumDirectPhotons;
}

/**
 * Tests whether the light affects the given bounding volume.
 * @param Bounds - The bounding volume to test.

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:1495

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FSpotLight::GetNumDirectPhotons

Source code excerpt:


/** Returns the number of direct photons to gather required by this light. */
int32 FSpotLight::GetNumDirectPhotons(float DirectPhotonDensity) const
{
	const float InfluenceSphereSurfaceAreaMillions = 4.0f * (float)PI * FMath::Square(Radius) / 1000000.0f;
	const float ConeSolidAngle = 2.0f * float(PI) * (1.0f - CosOuterConeAngle);
	// Find the fraction of the sphere's surface area that is inside the cone
	const float ConeSurfaceAreaSphereFraction = ConeSolidAngle / (4.0f * (float)PI);
	// Gather enough photons to meet DirectPhotonDensity on the spherical cap at the influence radius of the spot light.
	const int32 NumDirectPhotons = FMath::TruncToInt(InfluenceSphereSurfaceAreaMillions * ConeSurfaceAreaSphereFraction * DirectPhotonDensity);
	return NumDirectPhotons == appTruncErrorCode ? INT_MAX : NumDirectPhotons;
}

/** Generates a direction sample from the light's domain */
void FSpotLight::SampleDirection(FLMRandomStream& RandomStream, FLightRay& SampleRay, FVector4f& LightSourceNormal, FVector2f& LightSurfacePosition, float& RayPDF, FLinearColor& Power) const
{

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:1640

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FRectLight::GetNumDirectPhotons

Source code excerpt:


/** Returns the number of direct photons to gather required by this light. */
int32 FRectLight::GetNumDirectPhotons(float DirectPhotonDensity) const
{
	const float InfluenceSphereSurfaceAreaMillions = 4.0f * (float)PI * FMath::Square(Radius) / 1000000.0f;
	// Find the fraction of the sphere's surface area that is inside the cone
	const float SurfaceAreaSphereFraction = 0.25f;
	// Gather enough photons to meet DirectPhotonDensity on the spherical cap at the influence radius of the spot light.
	const int32 NumDirectPhotons = FMath::TruncToInt(InfluenceSphereSurfaceAreaMillions * SurfaceAreaSphereFraction * DirectPhotonDensity);
	return NumDirectPhotons == appTruncErrorCode ? INT_MAX : NumDirectPhotons;
}

/** Validates a surface sample given the position that sample is affecting. */
void FRectLight::ValidateSurfaceSample(const FVector4f& Point, FLightSurfaceSample& Sample) const
{}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:2198

Scope (from outer to inner):

file
namespace    Lightmass
function     int32 FMeshAreaLight::GetNumDirectPhotons

Source code excerpt:


/** Returns the number of direct photons to gather required by this light. */
int32 FMeshAreaLight::GetNumDirectPhotons(float DirectPhotonDensity) const
{
	// Gather enough photons to meet DirectPhotonDensity at the influence radius of the mesh area light.
	// Clamp the influence radius to the importance or scene radius for the purposes of emitting photons
	// This prevents huge mesh area lights from emitting more photons than are needed
	const float InfluenceSphereSurfaceAreaMillions = 4.0f * (float)PI * FMath::Square(FMath::Min(ImportanceBounds.SphereRadius, InfluenceRadius)) / 1000000.0f;
	const int32 NumDirectPhotons = FMath::TruncToInt(InfluenceSphereSurfaceAreaMillions * DirectPhotonDensity);
	return NumDirectPhotons == appTruncErrorCode ? INT_MAX : NumDirectPhotons;
}

/** Initializes the mesh area light with primitives */
void FMeshAreaLight::SetPrimitives(
	const TArray<FMeshLightPrimitive>& InPrimitives, 

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:138

Scope (from outer to inner):

file
namespace    Lightmass
class        class FLight : public FLightData

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const = 0;

	/**
	 * Tests whether the light affects the given bounding volume.
	 * @param Bounds - The bounding volume to test.
	 * @return True if the light affects the bounding volume
	 */

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:249

Scope (from outer to inner):

file
namespace    Lightmass
class        class FDirectionalLight : public FLight, public FDirectionalLightData

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const;

	/** Generates a direction sample from the light's domain */
	virtual void SampleDirection(FLMRandomStream& RandomStream, FLightRay& SampleRay, FVector4f& LightSourceNormal, FVector2f& LightSurfacePosition, float& RayPDF, FLinearColor& Power) const;

	/** Gives the light an opportunity to precalculate information about the indirect path rays that will be used to generate new directions. */
	virtual void CachePathRays(const TArray<FIndirectPathRay>& IndirectPathRays);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:346

Scope (from outer to inner):

file
namespace    Lightmass
class        class FPointLight : public FLight, public FPointLightData

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const;

	/**
	 * Tests whether the light affects the given bounding volume.
	 * @param Bounds - The bounding volume to test.
	 * @return True if the light affects the bounding volume
	 */

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:440

Scope (from outer to inner):

file
namespace    Lightmass
class        class FSpotLight : public FPointLight, public FSpotLightData

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const;

	/** Generates a direction sample from the light's domain */
	virtual void SampleDirection(FLMRandomStream& RandomStream, FLightRay& SampleRay, FVector4f& LightSourceNormal, FVector2f& LightSurfacePosition, float& RayPDF, FLinearColor& Power) const;

	/** Generates a direction sample from the light based on the given rays */
	virtual void SampleDirection(

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:469

Scope (from outer to inner):

file
namespace    Lightmass
class        class FRectLight : public FPointLight, public FRectLightData

Source code excerpt:


	virtual FLinearColor	GetDirectIntensity(const FVector4f& Point, bool bCalculateForIndirectLighting) const override;
	virtual int32			GetNumDirectPhotons(float DirectPhotonDensity) const override;
	virtual void			ValidateSurfaceSample(const FVector4f& Point, FLightSurfaceSample& Sample) const override;
	virtual FVector4f		LightCenterPosition(const FVector4f& ReceivingPosition, const FVector4f& ReceivingNormal) const override;
	virtual bool			AffectsBounds(const FBoxSphereBounds3f& Bounds) const override;
	virtual bool			BehindSurface(const FVector4f& TrianglePoint, const FVector4f& TriangleNormal) const override;
	virtual FVector4f		GetDirectLightingDirection(const FVector4f& Point, const FVector4f& PointNormal) const override;

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:507

Scope (from outer to inner):

file
namespace    Lightmass
class        class FSkyLight : public FLight, public FSkyLightData
function     virtual int32 GetNumDirectPhotons

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const
	{ checkf(0, TEXT("GetNumDirectPhotons is not supported for skylights")); return 0; }

	/** Generates a direction sample from the light's domain */
	virtual void SampleDirection(FLMRandomStream& RandomStream, class FLightRay& SampleRay, FVector4f& LightSourceNormal, FVector2f& LightSurfacePosition, float& RayPDF, FLinearColor& Power) const
	{ checkf(0, TEXT("SampleDirection is not supported for skylights")); }

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.h:606

Scope (from outer to inner):

file
namespace    Lightmass
class        class FMeshAreaLight : public FLight

Source code excerpt:


	/** Returns the number of direct photons to gather required by this light. */
	virtual int32 GetNumDirectPhotons(float DirectPhotonDensity) const;

	/** Initializes the mesh area light with primitives */
	void SetPrimitives(
		const TArray<FMeshLightPrimitive>& InPrimitives, 
		float EmissiveLightFalloffExponent, 
		float EmissiveLightExplicitInfluenceRadius,

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

Scope (from outer to inner):

file
namespace    Lightmass
function     FStaticLightingSystem::FStaticLightingSystem

Source code excerpt:

			Scene.PhotonMappingSettings.IndirectPhotonEmitDiskRadius,
			Scene.SceneConstants.LightGridSize,
			Scene.PhotonMappingSettings.DirectPhotonDensity,
			Scene.PhotonMappingSettings.DirectPhotonDensity * Scene.PhotonMappingSettings.OutsideImportanceVolumeDensityScale);

		Lights.Add(&InScene.DirectionalLights[LightIndex]);
	}
	
	// Initialize lights and add them to the solver's Lights array
	for (int32 LightIndex = 0; LightIndex < InScene.PointLights.Num(); LightIndex++)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PhotonMapping.cpp:42

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::InitializePhotonSettings

Source code excerpt:

		for (int32 LightIndex = 0; LightIndex < Lights.Num(); LightIndex++)
		{
			Stats.NumFirstPassPhotonsRequested += Lights[LightIndex]->GetNumDirectPhotons(PhotonMappingSettings.DirectPhotonDensity);
		}

		NumDirectPhotonsToEmit = FMath::Min<uint64>(Stats.NumFirstPassPhotonsRequested, (uint64)MaxNumDirectPhotonsToEmit);
		if (NumDirectPhotonsToEmit == MaxNumDirectPhotonsToEmit)
		{
			LogSolverMessage(FString::Printf(TEXT("Clamped the number of direct photons to emit to %.3f million, from %.3f million requested."), MaxNumDirectPhotonsToEmit / 1000000.0f, Stats.NumFirstPassPhotonsRequested / 1000000.0f));

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PhotonMapping.cpp:52

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::InitializePhotonSettings

Source code excerpt:

	}

	DirectIrradiancePhotonFraction = FMath::Clamp(Scene.PhotonMappingSettings.DirectIrradiancePhotonDensity / Scene.PhotonMappingSettings.DirectPhotonDensity, 0.0f, 1.0f);

	// Calculate numbers of photons to gather based on the scene using the given photon densities, the scene's surface area and the importance volume's surface area
	float SceneSurfaceAreaMillionUnits = FMath::Max(AggregateMesh->GetSurfaceArea() / 1000000.0f, DELTA);
	float SceneSurfaceAreaMillionUnitsEstimate = FMath::Max(4.0f * (float)PI * SceneBounds.SphereRadius * SceneBounds.SphereRadius / 1000000.0f, DELTA);
	float SceneSurfaceAreaMillionUnitsEstimateDiff = SceneSurfaceAreaMillionUnitsEstimate > DELTA ? ( SceneSurfaceAreaMillionUnits / SceneSurfaceAreaMillionUnitsEstimate * 100.0f ) : 0.0f;
	LogSolverMessage(FString::Printf(TEXT("Scene surface area calculated at %.3f million units (%.3f%% of the estimated %.3f million units)"), SceneSurfaceAreaMillionUnits, SceneSurfaceAreaMillionUnitsEstimateDiff, SceneSurfaceAreaMillionUnitsEstimate));

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PhotonMapping.cpp:211

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::EmitDirectPhotons

Source code excerpt:

	{
		const FLight* CurrentLight = Lights[LightIndex];
		const int32 LightNumDirectPhotons = CurrentLight->GetNumDirectPhotons(PhotonMappingSettings.DirectPhotonDensity);
		LightDistribution.LightPDFs.Add(LightNumDirectPhotons);
	}

	if (Lights.Num() > 0)
	{
		// Compute the Cumulative Distribution Function for our step function of light powers

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

Scope (from outer to inner):

file
namespace    Lightmass
class        class FPhotonMappingSettings

Source code excerpt:

	 * Density of direct photons to emit per light, in number of photons per million surface area units.
	 */
	float DirectPhotonDensity;

	/** Density of direct photons which have irradiance cached at their position, in number of photons per million surface area units. */
	float DirectIrradiancePhotonDensity;

	/** Distance to use when searching for direct photons. */
	float DirectPhotonSearchDistance;