MaxTransitionDistanceWorldSpace
MaxTransitionDistanceWorldSpace
#Overview
name: MaxTransitionDistanceWorldSpace
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 14
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of MaxTransitionDistanceWorldSpace is to control the maximum distance for shadow transitions in the static lighting system of Unreal Engine 5. This setting is primarily used in the rendering system, specifically for static shadow calculations.
This variable is utilized by the Lightmass subsystem, which is responsible for precalculating static lighting in Unreal Engine. It’s particularly important for the signed distance field shadow mapping technique.
The value of this variable is set in the Lightmass configuration file (GLightmassIni). It’s read from the “DevOptions.StaticShadows” section of this configuration file.
MaxTransitionDistanceWorldSpace interacts with several other variables, including:
- ApproximateHighResTexelsPerMaxTransitionDistance
- MinDistanceFieldUpsampleFactor
- StaticLightingLevelScale
Developers should be aware that:
- This variable directly affects the precision and maximum penumbra size that can be reconstructed from the distance field.
- Larger values decrease precision but allow for larger penumbra sizes.
- It’s scaled by the StaticLightingLevelScale, which means it adjusts based on the overall scale of the level.
Best practices when using this variable include:
- Carefully balancing it with ApproximateHighResTexelsPerMaxTransitionDistance to achieve the desired quality and performance.
- Considering the scale of your level when setting this value, as it’s in world space units.
- Testing different values to find the optimal balance between shadow quality and performance for your specific scene.
- Being mindful of its impact on build times, as larger values can increase the time needed for static lighting calculations.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseLightmass.ini:143, section: [DevOptions.StaticShadows]
- INI Section:
DevOptions.StaticShadows
- Raw value:
50
- Is Array:
False
#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:2315
Scope (from outer to inner):
file
function void FLightmassExporter::WriteSceneSettings
Source code excerpt:
VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.StaticShadows"), TEXT("bAllowSignedDistanceFieldShadows"), bConfigBool, GLightmassIni));
Scene.ShadowSettings.bAllowSignedDistanceFieldShadows = bConfigBool;
VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticShadows"), TEXT("MaxTransitionDistanceWorldSpace"), Scene.ShadowSettings.MaxTransitionDistanceWorldSpace, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.StaticShadows"), TEXT("ApproximateHighResTexelsPerMaxTransitionDistance"), Scene.ShadowSettings.ApproximateHighResTexelsPerMaxTransitionDistance, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.StaticShadows"), TEXT("MinDistanceFieldUpsampleFactor"), Scene.ShadowSettings.MinDistanceFieldUpsampleFactor, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticShadows"), TEXT("StaticShadowDepthMapTransitionSampleDistanceX"), Scene.ShadowSettings.StaticShadowDepthMapTransitionSampleDistanceX, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticShadows"), TEXT("StaticShadowDepthMapTransitionSampleDistanceY"), Scene.ShadowSettings.StaticShadowDepthMapTransitionSampleDistanceY, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.StaticShadows"), TEXT("StaticShadowDepthMapSuperSampleFactor"), Scene.ShadowSettings.StaticShadowDepthMapSuperSampleFactor, GLightmassIni));
VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.StaticShadows"), TEXT("StaticShadowDepthMapMaxSamples"), Scene.ShadowSettings.StaticShadowDepthMapMaxSamples, GLightmassIni));
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/ImportExport/LightmassScene.cpp:523
Scope (from outer to inner):
file
namespace Lightmass
function void FScene::ApplyStaticLightingScale
Source code excerpt:
VolumeDistanceFieldSettings.VoxelSize *= SceneConstants.StaticLightingLevelScale;
VolumeDistanceFieldSettings.VolumeMaxDistance *= SceneConstants.StaticLightingLevelScale;
ShadowSettings.MaxTransitionDistanceWorldSpace *= SceneConstants.StaticLightingLevelScale;
ShadowSettings.StaticShadowDepthMapTransitionSampleDistanceX *= SceneConstants.StaticLightingLevelScale;
ShadowSettings.StaticShadowDepthMapTransitionSampleDistanceY *= SceneConstants.StaticLightingLevelScale;
IrradianceCachingSettings.RecordRadiusScale *= SceneConstants.StaticLightingLevelScale;
IrradianceCachingSettings.MaxRecordRadius *= SceneConstants.StaticLightingLevelScale;
// Photon mapping does not scale down properly, so this is disabled
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:1928
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingTextureSpace
Source code excerpt:
// The result is that small, high resolution meshes will not upsample as much, since they don't need it,
// But large, low resolution meshes will upsample a lot.
const int32 TargetUpsampleFactor = FMath::TruncToInt(ShadowSettings.ApproximateHighResTexelsPerMaxTransitionDistance / (RightTriangleSide * ShadowSettings.MaxTransitionDistanceWorldSpace));
// Round up to the nearest odd factor, so each destination texel has a high resolution source texel at its center
// Clamp the upscale factor to be less than 13, since the quality improvements of upsampling higher than that are negligible.
UpsampleFactor = FMath::Clamp(TargetUpsampleFactor - TargetUpsampleFactor % 2 + 1, ShadowSettings.MinDistanceFieldUpsampleFactor, 13);
}
MappingContext.Stats.AccumulatedSignedDistanceFieldUpsampleFactors += UpsampleFactor;
MappingContext.Stats.NumSignedDistanceFieldCalculations++;
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2354
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingTextureSpace
Source code excerpt:
}
// Scatter to all distance field texels within MaxTransitionDistanceWorldSpace, rounded up.
// This is an approximation to the actual set of distance field texels that are within MaxTransitionDistanceWorldSpace that tends to work out well.
// Apply a clamp to avoid a performance cliff with some texels, whose adjacent texel in lightmap space is actually far away in world space
const int32 NumLowResScatterTexelsY = FMath::Min(FMath::TruncToInt(ShadowSettings.MaxTransitionDistanceWorldSpace / (WorldSpacePerHighResTexelY * UpsampleFactor)) + 1, 100);
const int32 NumLowResScatterTexelsX = FMath::Min(FMath::TruncToInt(ShadowSettings.MaxTransitionDistanceWorldSpace / (WorldSpacePerHighResTexelX * UpsampleFactor)) + 1, 100);
MappingContext.Stats.NumSignedDistanceFieldScatters++;
for (int32 ScatterOffsetY = -NumLowResScatterTexelsY; ScatterOffsetY <= NumLowResScatterTexelsY; ScatterOffsetY++)
{
const int32 LowResScatterY = LowResY + ScatterOffsetY;
if (LowResScatterY < 0 || LowResScatterY >= TextureMapping->CachedSizeY)
{
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2440
Scope: file
Source code excerpt:
// World space distance from the distance field texel to the nearest shadow transition
const float TransitionDistance = (ScatterPosition - HighResSample.GetPosition()).Size3();
const float NormalizedDistance = FMath::Clamp(TransitionDistance / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.0f, 1.0f);
FSignedDistanceFieldShadowSample& FinalShadowSample = (*ShadowMapData)(LowResScatterX, LowResScatterY);
// If LowResScatterSample.IsMapped() is true, the distance field texel must be mapped.
checkSlow(FinalShadowSample.bIsMapped);
// Only write to distance field texels whose existing transition distance is further than the transition distance being scattered.
if (NormalizedDistance * .5f < FMath::Abs(FinalShadowSample.Distance - .5f))
{
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2463
Scope: file
Source code excerpt:
// World space distance from center of penumbra to fully shadowed or fully lit transition
const float PenumbraSize = HighResSample.GetOccluderDistance() * Light->LightSourceRadius / (ReceiverDistanceFromLight - HighResSample.GetOccluderDistance());
// Normalize the penumbra size so it is a fraction of MaxTransitionDistanceWorldSpace
FinalShadowSample.PenumbraSize = FMath::Clamp(PenumbraSize / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.01f, 1.0f);
}
}
}
}
}
}
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2491
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
const FLight* Light) const
{
const FBoxSphereBounds3f MeshInfluenceBounds(TextureMapping->Mesh->BoundingBox.ExpandBy(ShadowSettings.MaxTransitionDistanceWorldSpace));
if (Light->AffectsBounds(MeshInfluenceBounds))
{
const FBoxSphereBounds3f SceneBounds = FBoxSphereBounds3f(AggregateMesh->GetBounds());
const FDirectionalLight* DirectionalLight = Light->GetDirectionalLight();
const FSpotLight* SpotLight = Light->GetSpotLight();
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2513
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
FStaticShadowDepthMap ShadowDepthMap;
int32 ShadowMapSizeX = FMath::TruncToInt(FMath::Max(LightSpaceImportanceBounds.GetExtent().X * 2.0f * 100.0f / ShadowSettings.MaxTransitionDistanceWorldSpace, 4.0f));
ShadowMapSizeX = ShadowMapSizeX == appTruncErrorCode ? INT_MAX : ShadowMapSizeX;
int32 ShadowMapSizeY = FMath::TruncToInt(FMath::Max(LightSpaceImportanceBounds.GetExtent().Y * 2.0f * 100.0f / ShadowSettings.MaxTransitionDistanceWorldSpace, 4.0f));
ShadowMapSizeY = ShadowMapSizeY == appTruncErrorCode ? INT_MAX : ShadowMapSizeY;
uint64 ShadowDepthMapMaxSamples = 4194304;
// Clamp the number of dominant shadow samples generated if necessary while maintaining aspect ratio
if ((uint64)ShadowMapSizeX * (uint64)ShadowMapSizeY > (uint64)ShadowDepthMapMaxSamples)
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2581
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
LIGHTINGSTAT(FScopedRDTSCTimer SearchTimer(MappingContext.Stats.SignedDistanceFieldSearchThreadTime));
FSignedDistanceFieldShadowMapData2D* ShadowMapData = new FSignedDistanceFieldShadowMapData2D(TextureMapping->CachedSizeX, TextureMapping->CachedSizeY);
const int32 TransitionSearchTexelRadiusX = FMath::TruncToInt(ShadowMapSizeX * ShadowSettings.MaxTransitionDistanceWorldSpace / LightSpaceImportanceBounds.GetSize().X);
const int32 TransitionSearchTexelRadiusY = FMath::TruncToInt(ShadowMapSizeY * ShadowSettings.MaxTransitionDistanceWorldSpace / LightSpaceImportanceBounds.GetSize().Y);
const float BoundsCellSizeX = (LightSpaceImportanceBounds.Max.X - LightSpaceImportanceBounds.Min.X) / ShadowMapSizeX;
const float BoundsCellSizeY = (LightSpaceImportanceBounds.Max.Y - LightSpaceImportanceBounds.Min.Y) / ShadowMapSizeY;
const float DepthBias = FMath::Max(BoundsCellSizeX, BoundsCellSizeY);
for (int32 Y = 0; Y < TextureMapping->CachedSizeY; Y++)
{
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2618
Scope (from outer to inner):
file
namespace Lightmass
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
const float SlopeScaledDepthBias = 4 * FMath::Max(BoundsCellSizeX * FMath::Abs(TanThetaX), BoundsCellSizeY * FMath::Abs(TanThetaY));
const bool bTexelVisible = TexelShadowMapDepth > SurfaceDepth - SlopeScaledDepthBias - DepthBias;
float ClosestTransition = ShadowSettings.MaxTransitionDistanceWorldSpace;
//float ClosestTransitionPenumbraSize = 1;
float MostShadowingTransition = 1;
float MostShadowingTransitionDistance = 1;
float MostShadowingTransitionPenumbraSize = 1;
for (int32 SearchY = FMath::Max(ShadowMapY - TransitionSearchTexelRadiusY, 0); SearchY < FMath::Min(ShadowMapY + TransitionSearchTexelRadiusY, ShadowMapSizeY); SearchY++)
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2641
Scope: file
Source code excerpt:
if (bTexelVisible)
{
const float SearchNormalizedDistance = FMath::Clamp(SearchTransition / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.0f, 1.0f);
const float SearchEncodedDistance = bTexelVisible ? (SearchNormalizedDistance) * .5f + .5f : .5f - SearchNormalizedDistance * .5f;
const float ReceiverDistanceFromLight = SurfaceDepth;
const float OccluderDistanceFromLight = bTexelVisible ? SearchShadowMapDepth : TexelShadowMapDepth;
// World space distance from center of penumbra to fully shadowed or fully lit transition
const float SearchPenumbraSize = (ReceiverDistanceFromLight - OccluderDistanceFromLight) * Light->LightSourceRadius / OccluderDistanceFromLight;
const float SearchEncodedPenumbraSize = FMath::Clamp(SearchPenumbraSize / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.01f, 1.0f);
const float SearchShadowing = FMath::Clamp(SearchEncodedDistance / SearchEncodedPenumbraSize - .5f / SearchEncodedPenumbraSize + .5f, 0.0f, 1.0f);
if (bSearchTexelVisible != bTexelVisible
/* && SearchTransition < ClosestTransition*/
&& SearchShadowing < MostShadowingTransition)
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2669
Scope (from outer to inner):
file
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
// World space distance from center of penumbra to fully shadowed or fully lit transition
const float PenumbraSize = (ReceiverDistanceFromLight - OccluderDistanceFromLight) * Light->LightSourceRadius / OccluderDistanceFromLight;
// Normalize the penumbra size so it is a fraction of MaxTransitionDistanceWorldSpace
ClosestTransitionPenumbraSize = FMath::Clamp(PenumbraSize / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.01f, 1.0f);
}*/
}
}
else
{
if (bSearchTexelVisible != bTexelVisible
&& SearchTransition < ClosestTransition)
{
ClosestTransition = SearchTransition;
}
}
}
}
FSignedDistanceFieldShadowSample& FinalShadowSample = (*ShadowMapData)(X, Y);
FinalShadowSample.bIsMapped = true;
FinalShadowSample.Distance = MostShadowingTransitionDistance;
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/TextureMapping.cpp:2692
Scope (from outer to inner):
file
function void FStaticLightingSystem::CalculateDirectSignedDistanceFieldLightingTextureMappingLightSpace
Source code excerpt:
if (!bTexelVisible)
{
const float NormalizedDistance = FMath::Clamp(ClosestTransition / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.0f, 1.0f);
// Encode the transition distance so that [.5,0] corresponds to [0,1] for shadowed texels, and [.5,1] corresponds to [0,1] for unshadowed texels.
// .5 of the encoded distance lies exactly on the shadow transition.
FinalShadowSample.Distance = bTexelVisible ? (NormalizedDistance) * .5f + .5f : .5f - NormalizedDistance * .5f;
const float ReceiverDistanceFromLight = SurfaceDepth;
const float OccluderDistanceFromLight = TexelShadowMapDepth;
const float PenumbraSize = (ReceiverDistanceFromLight - OccluderDistanceFromLight) * Light->LightSourceRadius / OccluderDistanceFromLight;
FinalShadowSample.PenumbraSize = FMath::Clamp(PenumbraSize / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.01f, 1.0f);
}
/*
const float NormalizedDistance = FMath::Clamp(ClosestTransition / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.0f, 1.0f);
// Encode the transition distance so that [.5,0] corresponds to [0,1] for shadowed texels, and [.5,1] corresponds to [0,1] for unshadowed texels.
// .5 of the encoded distance lies exactly on the shadow transition.
FinalShadowSample.Distance = bTexelVisible ? (NormalizedDistance) * .5f + .5f : .5f - NormalizedDistance * .5f;
if (bTexelVisible)
{
FinalShadowSample.PenumbraSize = ClosestTransitionPenumbraSize;
}
else
{
const float ReceiverDistanceFromLight = SurfaceDepth;
const float OccluderDistanceFromLight = TexelShadowMapDepth;
const float PenumbraSize = (ReceiverDistanceFromLight - OccluderDistanceFromLight) * Light->LightSourceRadius / OccluderDistanceFromLight;
FinalShadowSample.PenumbraSize = FMath::Clamp(PenumbraSize / ShadowSettings.MaxTransitionDistanceWorldSpace, 0.01f, 1.0f);
}
*/
}
}
}
ShadowMaps.Add(Light, ShadowMapData);
}
}
}
/**
* Estimate direct lighting using the direct photon map.
* This is only useful for debugging what the final gather rays see.
*/
void FStaticLightingSystem::CalculateDirectLightingTextureMappingPhotonMap(
FStaticLightingTextureMapping* TextureMapping,
FStaticLightingMappingContext& MappingContext,
FGatheredLightMapData2D& LightMapData,
TMap<const FLight*, FShadowMapData2D*>& ShadowMaps,
const FTexelToVertexMap& TexelToVertexMap,
bool bDebugThisMapping) const
{
#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Public/SceneExport.h:472
Scope (from outer to inner):
file
namespace Lightmass
class class FStaticShadowSettings
Source code excerpt:
* Larger distances decrease precision but increase the maximum penumbra size that can be reconstructed from the distance field.
*/
float MaxTransitionDistanceWorldSpace;
/**
* The number of high resolution samples to calculate per MaxTransitionDistanceWorldSpace.
* Higher values increase the distance field reconstruction quality, at the cost of longer build times.
*/
int32 ApproximateHighResTexelsPerMaxTransitionDistance;
/**
* The minimum upsample factor to calculate the high resolution samples at.
* Larger values increase distance field reconstruction quality on small, high resolution meshes, at the cost of longer build times.
*/
int32 MinDistanceFieldUpsampleFactor;