VisibilityNormalOffsetSampleRadiusScale

VisibilityNormalOffsetSampleRadiusScale

#Overview

name: VisibilityNormalOffsetSampleRadiusScale

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

#Summary

#Usage in the C++ source code

The purpose of VisibilityNormalOffsetSampleRadiusScale is to adjust the offset distance along the normal direction when performing visibility calculations in Unreal Engine’s static lighting system. This variable is used to scale the sample radius, which helps to prevent incorrect self-intersection and self-occlusion issues in highly curved surfaces or complex geometry.

This setting variable is primarily used in Unreal Engine’s Lightmass system, which is responsible for generating static lighting data. It is part of the static lighting and global illumination calculations, specifically in the following subsystems:

  1. Lightmass Exporter
  2. Static Lighting System
  3. Final Gather
  4. Lighting Mesh
  5. Texture Mapping

The value of this variable is typically set in the Lightmass configuration file (GLightmassIni) under the “DevOptions.StaticLightingSceneConstants” section. It is read and applied during the scene export process for Lightmass calculations.

VisibilityNormalOffsetSampleRadiusScale interacts with other visibility-related variables, such as VisibilityRayOffsetDistance, VisibilityNormalOffsetDistance, and VisibilityTangentOffsetSampleRadiusScale. These variables work together to fine-tune the ray casting and intersection calculations in the static lighting system.

Developers must be aware that adjusting this variable can have a significant impact on the quality and accuracy of static lighting calculations. Increasing the value may help reduce self-intersection artifacts but could potentially introduce light leaking or other visual issues if set too high.

Best practices when using this variable include:

  1. Keep the value relatively small (typically less than 1.0) to avoid introducing noticeable artifacts.
  2. Test thoroughly after making changes, as its effects can vary depending on the scene geometry and lighting conditions.
  3. Consider adjusting this value in conjunction with other visibility-related variables for optimal results.
  4. Document any changes made to this variable, as it can affect the reproducibility of lighting builds across different machines or project versions.

#Setting Variables

#References In INI files

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

#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:2183

Scope (from outer to inner):

file
function     void FLightmassExporter::WriteSceneSettings

Source code excerpt:

		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("VisibilityRayOffsetDistance"), Scene.SceneConstants.VisibilityRayOffsetDistance, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("VisibilityNormalOffsetDistance"), Scene.SceneConstants.VisibilityNormalOffsetDistance, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("VisibilityNormalOffsetSampleRadiusScale"), Scene.SceneConstants.VisibilityNormalOffsetSampleRadiusScale, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("VisibilityTangentOffsetSampleRadiusScale"), Scene.SceneConstants.VisibilityTangentOffsetSampleRadiusScale, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("SmallestTexelRadius"), Scene.SceneConstants.SmallestTexelRadius, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("LightGridSize"), Scene.SceneConstants.LightGridSize, GLightmassIni));
	}
	{
		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.StaticLightingMaterial"), TEXT("bUseDebugMaterial"), bConfigBool, GLightmassIni));

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

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::IntersectLightRays

Source code excerpt:


		const float RayStartNormalBiasScale = RayBiasMode == RBM_ConstantNormalOffset
			? SceneConstants.VisibilityNormalOffsetSampleRadiusScale 
			: (SceneConstants.VisibilityTangentOffsetSampleRadiusScale * TangentPathDirection.Z);

		// Apply various offsets to the start of the ray.
		// The offset along the ray direction is to avoid incorrect self-intersection due to floating point precision.
		// The offset along the normal is to push self-intersection patterns (like triangle shape) on highly curved surfaces onto the backfaces.
		FVector3f RayStart = Vertex.WorldPosition

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

Scope: file

Source code excerpt:

						// And set the record radius to the minimum to contain the error.
						// Center of the texel offset along the normal
						const FVector4f TexelCenterOffset = Vertex.WorldPosition + Vertex.TriangleNormal * SampleRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale;
						// Vector from the center to one of the corners of the texel
						// The FMath::Sqrt(.5f) is to normalize (Vertex.TriangleTangentX + Vertex.TriangleTangentY), which are orthogonal unit vectors.
						const FVector4f CornerOffset = FMath::Sqrt(.5f) * (Vertex.TriangleTangentX + Vertex.TriangleTangentY) * SampleRadius * SceneConstants.VisibilityTangentOffsetSampleRadiusScale;
						const FLightRay TexelRay(
							TexelCenterOffset + CornerOffset,
							TexelCenterOffset - CornerOffset,

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingMesh.cpp:442

Scope: file

Source code excerpt:

									// Offset the light primitives by a fraction of the texel's bounding radius to avoid incorrect self-occlusion,
									// Since the surface of the light is actually a mesh
									FMath::Sqrt(TexelBoundingRadiusSquared) * Scene.SceneConstants.VisibilityNormalOffsetSampleRadiusScale);
							}
						}
					}
				}

				TArray<TArray<FMeshLightPrimitive>> TrimmedEmissivePrimitives;

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

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::ValidateSettings
function     FVector2f FStaticLightingSystem::CalculatePointAreaShadowing

Source code excerpt:

				Vertex.WorldPosition 
					+ LightVector.GetSafeNormal() * SceneConstants.VisibilityRayOffsetDistance 
					+ NormalForOffset * SampleRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale 
					+ SampleOffset,
				Vertex.WorldPosition + LightVector,
				Mapping,
				Light
				);

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/LightingSystem.inl:141

Scope (from outer to inner):

file
function     void FStaticLightingSystem::CalculateApproximateDirectLighting

Source code excerpt:


					const FVector4f StartOffset = LightVector.GetSafeNormal() * SceneConstants.VisibilityRayOffsetDistance
						+ NormalForOffset * SampleRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale;

					const FLightRay LightRay(
						// Offset the start of the ray by some fraction along the direction of the ray and some fraction along the vertex normal.
						Vertex.WorldPosition
						+ StartOffset,
						Vertex.WorldPosition + LightVector,

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

Scope (from outer to inner):

file
namespace    Lightmass
function     FGatheredLightSample FStaticLightingSystem::InterpolatePrecomputedVolumeIncidentRadiance

Source code excerpt:

			FLightRayIntersection Intersection;
			const FLightRay SampleRay
				(Vertex.WorldPosition + Vertex.WorldTangentZ * SceneConstants.VisibilityNormalOffsetSampleRadiusScale * SampleRadius, 
				VolumeSample.GetPosition(), 
				nullptr, 
				nullptr);
			AggregateMesh->IntersectLightRay(SampleRay, false, false, false, RayCache, Intersection);
			if (!Intersection.bIntersects)
			*/
			{
				const float SampleWeight = (1.0f - (Vertex.WorldPosition - VolumeSample.GetPosition()).Size3() / VolumeSample.GetRadius()) / VolumeSample.GetRadius();
				TotalWeight += SampleWeight;
			}
		}
	}

	if (TotalWeight > DELTA)
	{

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

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::AdjustRepresentativeSurfelForTexelsTextureMapping

Source code excerpt:

				{
					const FFullStaticLightingVertex FullVertex = TexelToVertex.GetFullVertex();
					const FVector4f TexelCenterOffset = FullVertex.WorldPosition + FullVertex.TriangleNormal * TexelToVertex.TexelRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale;

					FLightRayIntersection Intersections[4];
					bool bHitBackfaces[4];

					FVector2f CornerSigns[4];
					CornerSigns[0] = FVector2f(1, 1);

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

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::AdjustRepresentativeSurfelForTexelsTextureMapping

Source code excerpt:

						const FVector4f OffsetShadingPosition = Intersections[IntersectionIndexForShadingPositionMovement].IntersectionVertex.WorldPosition
							// Move along the intersecting surface's normal but also away from the texel a bit to prevent incorrect self occlusion
							+ (Intersections[IntersectionIndexForShadingPositionMovement].IntersectionVertex.WorldTangentZ + TexelToVertex.TriangleNormal) * 0.5f * TexelToVertex.TexelRadius * SceneConstants.VisibilityNormalOffsetSampleRadiusScale;

						// Project back onto plane of texel to avoid incorrect self occlusion
						TexelToVertex.WorldPosition = OffsetShadingPosition + TexelToVertex.TriangleNormal * Dot3(TexelToVertex.TriangleNormal, TexelToVertex.WorldPosition - OffsetShadingPosition);

						TexelToVertex.SampleRadius = (OffsetShadingPosition - Intersections[IntersectionIndexForShadingPositionMovement].IntersectionVertex.WorldPosition).Size3() / FMath::Sqrt(2.0f);
					}

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

Scope (from outer to inner):

file
namespace    Lightmass
class        class FStaticLightingSceneConstants

Source code excerpt:

	 * This is applied instead of VisibilityNormalOffsetDistance whenever sample radius is known as it adapts to differently sized texels.
	 */
	float VisibilityNormalOffsetSampleRadiusScale;

	/** 
	 * Fraction of the sample radius to offset the origin of the ray in the tangent XY plane, based on the direction of the ray.
	 * This is only used when bAccountForTexelSize is true.
	 */
	float VisibilityTangentOffsetSampleRadiusScale;