Files
UnrealEngine/Engine/Source/Programs/UnrealLightmass/Private/Lighting/Radiosity.cpp
2025-05-18 13:04:45 +08:00

826 lines
31 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Raster.h"
#include "LightingSystem.h"
#include "LightmassSwarm.h"
#include "Misc/Compression.h"
#include "HAL/RunnableThread.h"
#include "HAL/PlatformProcess.h"
#include "TextureMappingSetup.h"
#include "Misc/Compression.h"
namespace Lightmass
{
bool bCompressRadiosityCachedData = false;
void FCompressedGatherHitPoints::Compress(const FGatherHitPoints& Source)
{
{
const int32 UncompressedSize = Source.GatherHitPointRanges.Num() * Source.GatherHitPointRanges.GetTypeSize();
TArray<uint8> TempCompressedMemory;
// Compressed can be slightly larger than uncompressed
TempCompressedMemory.Empty(UncompressedSize * 4 / 3);
TempCompressedMemory.AddUninitialized(UncompressedSize * 4 / 3);
int32 CompressedSize = TempCompressedMemory.Num() * TempCompressedMemory.GetTypeSize();
verify(FCompression::CompressMemory(
NAME_Zlib,
TempCompressedMemory.GetData(),
CompressedSize,
Source.GatherHitPointRanges.GetData(),
UncompressedSize,
COMPRESS_BiasSpeed));
GatherHitPointRanges.Empty(CompressedSize);
GatherHitPointRanges.AddUninitialized(CompressedSize);
FPlatformMemory::Memcpy(GatherHitPointRanges.GetData(), TempCompressedMemory.GetData(), CompressedSize);
GatherHitPointRangesUncompressedSize = UncompressedSize;
}
{
const int32 UncompressedSize = Source.GatherHitPointData.Num() * Source.GatherHitPointData.GetTypeSize();
TArray<uint8> TempCompressedMemory;
// Compressed can be slightly larger than uncompressed
TempCompressedMemory.Empty(UncompressedSize * 4 / 3);
TempCompressedMemory.AddUninitialized(UncompressedSize * 4 / 3);
int32 CompressedSize = TempCompressedMemory.Num() * TempCompressedMemory.GetTypeSize();
verify(FCompression::CompressMemory(
NAME_Zlib,
TempCompressedMemory.GetData(),
CompressedSize,
Source.GatherHitPointData.GetData(),
UncompressedSize));
GatherHitPointData.Empty(CompressedSize);
GatherHitPointData.AddUninitialized(CompressedSize);
FPlatformMemory::Memcpy(GatherHitPointData.GetData(), TempCompressedMemory.GetData(), CompressedSize);
GatherHitPointDataUncompressedSize = UncompressedSize;
}
}
void FCompressedGatherHitPoints::Decompress(FGatherHitPoints& Dest) const
{
Dest.GatherHitPointRanges.Reset(GatherHitPointRangesUncompressedSize);
Dest.GatherHitPointRanges.AddUninitialized(GatherHitPointRangesUncompressedSize);
verify(FCompression::UncompressMemory(
NAME_Zlib,
Dest.GatherHitPointRanges.GetData(),
GatherHitPointRangesUncompressedSize,
GatherHitPointRanges.GetData(),
GatherHitPointRanges.Num()));
Dest.GatherHitPointData.Reset(GatherHitPointDataUncompressedSize);
Dest.GatherHitPointData.AddUninitialized(GatherHitPointDataUncompressedSize);
verify(FCompression::UncompressMemory(
NAME_Zlib,
Dest.GatherHitPointData.GetData(),
GatherHitPointDataUncompressedSize,
GatherHitPointData.GetData(),
GatherHitPointData.Num()));
}
void FCompressedInfluencingRecords::Compress(const FInfluencingRecords& Source)
{
{
const int32 UncompressedSize = Source.Ranges.Num() * Source.Ranges.GetTypeSize();
TArray<uint8> TempCompressedMemory;
// Compressed can be slightly larger than uncompressed
TempCompressedMemory.Empty(UncompressedSize * 4 / 3);
TempCompressedMemory.AddUninitialized(UncompressedSize * 4 / 3);
int32 CompressedSize = TempCompressedMemory.Num() * TempCompressedMemory.GetTypeSize();
verify(FCompression::CompressMemory(
NAME_Zlib,
TempCompressedMemory.GetData(),
CompressedSize,
Source.Ranges.GetData(),
UncompressedSize,
COMPRESS_BiasSpeed));
Ranges.Empty(CompressedSize);
Ranges.AddUninitialized(CompressedSize);
FPlatformMemory::Memcpy(Ranges.GetData(), TempCompressedMemory.GetData(), CompressedSize);
RangesUncompressedSize = UncompressedSize;
}
{
const int32 UncompressedSize = Source.Data.Num() * Source.Data.GetTypeSize();
TArray<uint8> TempCompressedMemory;
// Compressed can be slightly larger than uncompressed
TempCompressedMemory.Empty(UncompressedSize * 4 / 3);
TempCompressedMemory.AddUninitialized(UncompressedSize * 4 / 3);
int32 CompressedSize = TempCompressedMemory.Num() * TempCompressedMemory.GetTypeSize();
verify(FCompression::CompressMemory(
NAME_Zlib,
TempCompressedMemory.GetData(),
CompressedSize,
Source.Data.GetData(),
UncompressedSize));
Data.Empty(CompressedSize);
Data.AddUninitialized(CompressedSize);
FPlatformMemory::Memcpy(Data.GetData(), TempCompressedMemory.GetData(), CompressedSize);
DataUncompressedSize = UncompressedSize;
}
}
void FCompressedInfluencingRecords::Decompress(FInfluencingRecords& Dest) const
{
Dest.Ranges.Reset(RangesUncompressedSize);
Dest.Ranges.AddUninitialized(RangesUncompressedSize);
verify(FCompression::UncompressMemory(
NAME_Zlib,
Dest.Ranges.GetData(),
RangesUncompressedSize,
Ranges.GetData(),
Ranges.Num()));
Dest.Data.Reset(DataUncompressedSize);
Dest.Data.AddUninitialized(DataUncompressedSize);
verify(FCompression::UncompressMemory(
NAME_Zlib,
Dest.Data.GetData(),
DataUncompressedSize,
Data.GetData(),
Data.Num()));
}
void FStaticLightingSystem::SetupRadiosity()
{
const double RadiosityStartTime = FPlatformTime::Seconds();
for(int32 ThreadIndex = 1; ThreadIndex < NumStaticLightingThreads; ThreadIndex++)
{
FMappingProcessingThreadRunnable* ThreadRunnable = new FMappingProcessingThreadRunnable(this, ThreadIndex, StaticLightingTask_RadiositySetup);
RadiositySetupThreads.Add(ThreadRunnable);
const FString ThreadName = FString::Printf(TEXT("RadiositySetupThread%u"), ThreadIndex);
ThreadRunnable->Thread = FRunnableThread::Create(ThreadRunnable, *ThreadName);
}
// Start the static lighting thread loop on the main thread, too.
// Once it returns, all static lighting mappings have begun processing.
RadiositySetupThreadLoop(0, true);
// Stop the static lighting threads.
for(int32 ThreadIndex = 0;ThreadIndex < RadiositySetupThreads.Num();ThreadIndex++)
{
// Wait for the thread to exit.
RadiositySetupThreads[ThreadIndex].Thread->WaitForCompletion();
// Check that it didn't terminate with an error.
RadiositySetupThreads[ThreadIndex].CheckHealth();
// Destroy the thread.
delete RadiositySetupThreads[ThreadIndex].Thread;
}
RadiositySetupThreads.Empty();
const float RadiosityDuration = FPlatformTime::Seconds() - RadiosityStartTime;
LogSolverMessage(FString::Printf(TEXT("Radiosity Setup %.1fs"), RadiosityDuration));
}
void FStaticLightingSystem::RadiositySetupThreadLoop(int32 ThreadIndex, bool bIsMainThread)
{
GSwarm->SendMessage( NSwarm::FTimingMessage( NSwarm::PROGSTATE_Preparing4, ThreadIndex ) );
bool bIsDone = false;
while (!bIsDone)
{
// Atomically read and increment the next mapping index to process.
const int32 MappingIndex = NextMappingToProcessRadiositySetup.Increment() - 1;
if (MappingIndex < AllMappings.Num())
{
// If this is the main thread, update progress and apply completed static lighting.
if (bIsMainThread)
{
// Check the health of all static lighting threads.
for (int32 ThreadIndexIter = 0; ThreadIndexIter < RadiositySetupThreads.Num(); ThreadIndexIter++)
{
RadiositySetupThreads[ThreadIndexIter].CheckHealth();
}
}
FStaticLightingTextureMapping* TextureMapping = AllMappings[MappingIndex]->GetTextureMapping();
if (TextureMapping)
{
RadiositySetupTextureMapping(TextureMapping);
}
}
else
{
// Processing has begun for all mappings.
bIsDone = true;
}
}
}
/** Caches irradiance photons on a single texture mapping. */
void FStaticLightingSystem::RadiositySetupTextureMapping(FStaticLightingTextureMapping* TextureMapping)
{
checkSlow(TextureMapping);
FStaticLightingMappingContext MappingContext(TextureMapping->Mesh,*this,&StartupDebugOutput);
LIGHTINGSTAT(FScopedRDTSCTimer CachingTime(MappingContext.Stats.RadiositySetupThreadTime));
const FBoxSphereBounds3f ImportanceBounds = Scene.GetImportanceBounds();
FTexelToVertexMap TexelToVertexMap(TextureMapping->SurfaceCacheSizeX, TextureMapping->SurfaceCacheSizeY);
bool bDebugThisMapping = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
bDebugThisMapping = TextureMapping == Scene.DebugMapping;
int32 SurfaceCacheDebugX = -1;
int32 SurfaceCacheDebugY = -1;
if (bDebugThisMapping)
{
SurfaceCacheDebugX = FMath::TruncToInt(Scene.DebugInput.LocalX / (float)TextureMapping->CachedSizeX * TextureMapping->SurfaceCacheSizeX);
SurfaceCacheDebugY = FMath::TruncToInt(Scene.DebugInput.LocalY / (float)TextureMapping->CachedSizeY * TextureMapping->SurfaceCacheSizeY);
}
#endif
RasterizeToSurfaceCacheTextureMapping(TextureMapping, bDebugThisMapping, TexelToVertexMap);
TextureMapping->SurfaceCacheLighting.Empty(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
TextureMapping->SurfaceCacheLighting.AddZeroed(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
TextureMapping->RadiositySurfaceCache[0].Empty(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
TextureMapping->RadiositySurfaceCache[0].AddZeroed(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
TextureMapping->RadiositySurfaceCache[1].Empty(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
TextureMapping->RadiositySurfaceCache[1].AddZeroed(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
const bool bCacheFinalGatherHitPoints = ImportanceTracingSettings.bCacheFinalGatherHitPointsForRadiosity && GeneralSettings.NumSkyLightingBounces > 0;
FGatherHitPoints& GatherHitPoints = TextureMapping->UncompressedGatherHitPoints;
if (bCacheFinalGatherHitPoints)
{
GatherHitPoints.GatherHitPointRanges.Empty(TextureMapping->SurfaceCacheSizeY * TextureMapping->SurfaceCacheSizeX / 4);
GatherHitPoints.GatherHitPointData.Empty(TextureMapping->SurfaceCacheSizeY * TextureMapping->SurfaceCacheSizeX / 4);
}
TLightingCache<FFinalGatherSample> RadiosityCache(TextureMapping->Mesh->BoundingBox, *this, 1);
FLMRandomStream RandomStream(0);
#if LIGHTMASS_DO_PROCESSING
if (GeneralSettings.NumSkyLightingBounces > 0)
{
for (int32 Y = 0; Y < TextureMapping->SurfaceCacheSizeY; Y++)
{
for (int32 X = 0; X < TextureMapping->SurfaceCacheSizeX; X++)
{
bool bDebugThisTexel = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
if (bDebugThisMapping
&& Y == SurfaceCacheDebugY
&& X == SurfaceCacheDebugX)
{
bDebugThisTexel = true;
}
#endif
const FTexelToVertex& TexelToVertex = TexelToVertexMap(X,Y);
if (TexelToVertex.TotalSampleWeight > 0.0f)
{
FFullStaticLightingVertex Vertex = TexelToVertex.GetFullVertex();
Vertex.ApplyVertexModifications(TexelToVertex.ElementIndex, MaterialSettings.bUseNormalMapsForLighting, TextureMapping->Mesh);
FFinalGatherSample SkyLighting;
FFinalGatherSample UnusedSecondLighting;
float UnusedBackfacingHitsFraction;
if (!RadiosityCache.InterpolateLighting(Vertex, true, false, 1.0f, SkyLighting, UnusedSecondLighting, UnusedBackfacingHitsFraction, MappingContext.DebugCacheRecords))
{
FFinalGatherSample UniformSampledIncomingRadiance;
TArray<FVector4f> ImportancePhotonDirections;
FLightingCacheGatherInfo GatherInfo;
const int32 NumAdaptiveRefinementLevels = GeneralSettings.IndirectLightingQuality <= 10 ? 1 : 2;
if (bCacheFinalGatherHitPoints)
{
GatherHitPoints.GatherHitPointRanges.Add(FArrayRange(GatherHitPoints.GatherHitPointData.Num()));
GatherInfo.HitPointRecorder = &GatherHitPoints;
}
UniformSampledIncomingRadiance = IncomingRadianceAdaptive<FFinalGatherSample>(
TextureMapping,
Vertex,
TexelToVertex.SampleRadius,
false,
TexelToVertex.ElementIndex,
2, /** BounceNumber */
RBM_ConstantNormalOffset,
GLM_GatherLightEmitted, /* Gather sky light and emissive only */
NumAdaptiveRefinementLevels,
1.0f,
CachedHemisphereSamplesForRadiosity[0],
CachedHemisphereSamplesForRadiosityUniforms[0],
1,
ImportancePhotonDirections,
MappingContext,
RandomStream,
GatherInfo,
true, /* bGatheringForCachedDirectLighting */
false);
float OverrideRadius = 0;
TLightingCache<FFinalGatherSample>::FRecord<FFinalGatherSample> NewRecord(
Vertex,
TexelToVertex.ElementIndex,
GatherInfo,
TexelToVertex.SampleRadius,
OverrideRadius,
IrradianceCachingSettings,
GeneralSettings,
UniformSampledIncomingRadiance,
FVector4f(0, 0, 0, 0),
FVector4f(0, 0, 0, 0)
);
// Add the incident radiance sample to the cache.
RadiosityCache.AddRecord(NewRecord, false, false);
}
}
}
}
}
FInfluencingRecords& InfluencingRecords = TextureMapping->InfluencingRecordsSurfaceCache;
if (bCacheFinalGatherHitPoints)
{
InfluencingRecords.Ranges.Empty(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
InfluencingRecords.Ranges.AddZeroed(TextureMapping->SurfaceCacheSizeX * TextureMapping->SurfaceCacheSizeY);
InfluencingRecords.Data.Empty(GatherHitPoints.GatherHitPointData.Num());
}
for (int32 Y = 0; Y < TextureMapping->SurfaceCacheSizeY; Y++)
{
for (int32 X = 0; X < TextureMapping->SurfaceCacheSizeX; X++)
{
bool bDebugThisTexel = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
if (bDebugThisMapping
&& Y == SurfaceCacheDebugY
&& X == SurfaceCacheDebugX)
{
bDebugThisTexel = true;
}
#endif
const FTexelToVertex& TexelToVertex = TexelToVertexMap(X,Y);
if (TexelToVertex.TotalSampleWeight > 0.0f)
{
FFullStaticLightingVertex CurrentVertex = TexelToVertex.GetFullVertex();
CurrentVertex.ApplyVertexModifications(TexelToVertex.ElementIndex, MaterialSettings.bUseNormalMapsForLighting, TextureMapping->Mesh);
const int32 SurfaceCacheIndex = Y * TextureMapping->SurfaceCacheSizeX + X;
FInfluencingRecordCollector RecordCollector(InfluencingRecords, SurfaceCacheIndex);
FInfluencingRecordCollector* RecordCollectorPtr = NULL;
if (bCacheFinalGatherHitPoints)
{
RecordCollectorPtr = &RecordCollector;
InfluencingRecords.Ranges[SurfaceCacheIndex] = FArrayRange(InfluencingRecords.Data.Num());
}
FLinearColor IncidentLighting = FLinearColor::Black;
FLinearColor IncidentLightingForRadiosity = FLinearColor::Black;
if (GeneralSettings.NumSkyLightingBounces > 0)
{
FFinalGatherSample SkyLighting;
FFinalGatherSample UnusedSecondLighting;
float UnusedBackfacingHitsFraction;
RadiosityCache.InterpolateLighting(CurrentVertex, false, false, IrradianceCachingSettings.SkyOcclusionSmoothnessReduction, SkyLighting, UnusedSecondLighting, UnusedBackfacingHitsFraction, MappingContext.DebugCacheRecords, RecordCollectorPtr);
if (GeneralSettings.ViewSingleBounceNumber < 0 || GeneralSettings.ViewSingleBounceNumber == 1)
{
IncidentLighting += SkyLighting.IncidentLighting + SkyLighting.StationarySkyLighting.IncidentLighting;
}
IncidentLightingForRadiosity += SkyLighting.IncidentLighting + SkyLighting.StationarySkyLighting.IncidentLighting;
}
if (ImportanceTracingSettings.bUseRadiositySolverForLightMultibounce)
{
FGatheredLightSample DirectLighting;
float Unused2;
TArray<FVector3f, TInlineAllocator<1>> VertexOffsets;
VertexOffsets.Add(FVector3f(0, 0, 0));
CalculateApproximateDirectLighting(CurrentVertex, TexelToVertex.TexelRadius, VertexOffsets, .1f, true, true, bDebugThisTexel, MappingContext, DirectLighting, Unused2);
IncidentLightingForRadiosity += DirectLighting.IncidentLighting;
}
TextureMapping->SurfaceCacheLighting[SurfaceCacheIndex] = IncidentLighting;
TextureMapping->RadiositySurfaceCache[0][SurfaceCacheIndex] = IncidentLightingForRadiosity;
}
}
}
if (bCacheFinalGatherHitPoints && bCompressRadiosityCachedData)
{
TextureMapping->CompressedGatherHitPoints.Compress(GatherHitPoints);
GatherHitPoints = FGatherHitPoints();
TextureMapping->CompressedInfluencingRecords.Compress(InfluencingRecords);
InfluencingRecords = FInfluencingRecords();
}
#endif
}
/** */
void FStaticLightingSystem::RunRadiosityIterations()
{
// First bounce done in setup
const int32 NumRadiosityIterations = FMath::Max(GeneralSettings.NumSkyLightingBounces - 1, 0);
if (NumRadiosityIterations > 0)
{
const double RadiosityStartTime = FPlatformTime::Seconds();
for(int32 ThreadIndex = 1; ThreadIndex < NumStaticLightingThreads; ThreadIndex++)
{
FMappingProcessingThreadRunnable* ThreadRunnable = new FMappingProcessingThreadRunnable(this, ThreadIndex, StaticLightingTask_RadiosityIterations);
RadiosityIterationThreads.Add(ThreadRunnable);
const FString ThreadName = FString::Printf(TEXT("RadiosityIterationThread%u"), ThreadIndex);
ThreadRunnable->Thread = FRunnableThread::Create(ThreadRunnable, *ThreadName);
}
// Start the static lighting thread loop on the main thread, too.
// Once it returns, all static lighting mappings have begun processing.
RadiosityIterationThreadLoop(0, true);
// Stop the static lighting threads.
for(int32 ThreadIndex = 0;ThreadIndex < RadiosityIterationThreads.Num();ThreadIndex++)
{
// Wait for the thread to exit.
RadiosityIterationThreads[ThreadIndex].Thread->WaitForCompletion();
// Check that it didn't terminate with an error.
RadiosityIterationThreads[ThreadIndex].CheckHealth();
// Destroy the thread.
delete RadiosityIterationThreads[ThreadIndex].Thread;
}
RadiosityIterationThreads.Empty();
size_t TemporariesSize = 0;
for (int32 MappingIndex = 0; MappingIndex < AllMappings.Num(); MappingIndex++)
{
TemporariesSize += AllMappings[MappingIndex]->FreeRadiosityTemporaries();
}
const float RadiosityDuration = FPlatformTime::Seconds() - RadiosityStartTime;
LogSolverMessage(FString::Printf(TEXT("Radiosity Iterations %.1fs with %.1fMb of cached data"), RadiosityDuration, TemporariesSize / 1024.0f / 1024.0f));
}
}
/** */
void FStaticLightingSystem::RadiosityIterationThreadLoop(int32 ThreadIndex, bool bIsMainThread)
{
const int32 NumRadiosityIterations = FMath::Max(GeneralSettings.NumSkyLightingBounces - 1, 0);
bool bIsDone = false;
while (!bIsDone)
{
// Atomically read and increment the next mapping index to process.
const int32 TaskIndex = NextMappingToProcessRadiosityIterations.Increment() - 1;
if (TaskIndex < AllMappings.Num() * NumRadiosityIterations)
{
const int32 PassIndex = TaskIndex / AllMappings.Num();
const int32 MappingIndex = TaskIndex - PassIndex * AllMappings.Num();
// If this is the main thread, update progress and apply completed static lighting.
if (bIsMainThread)
{
// Check the health of all static lighting threads.
for (int32 ThreadIndexIter = 0; ThreadIndexIter < RadiosityIterationThreads.Num(); ThreadIndexIter++)
{
RadiosityIterationThreads[ThreadIndexIter].CheckHealth();
}
}
if (PassIndex > 0)
{
// Sleep-loop until other threads have completed the previous pass
while (NumCompletedRadiosityIterationMappings[PassIndex - 1].GetValue() < AllMappings.Num())
{
FPlatformProcess::Sleep(0.0f);
}
}
FStaticLightingTextureMapping* TextureMapping = AllMappings[MappingIndex]->GetTextureMapping();
if (TextureMapping)
{
RadiosityIterationTextureMapping(TextureMapping, PassIndex);
// Make sure writes to mapping data are seen on other threads before the change to NumCompletedRadiosityIterationMappings
FPlatformMisc::MemoryBarrier();
NumCompletedRadiosityIterationMappings[PassIndex].Increment();
}
}
else
{
// Processing has begun for all mappings.
bIsDone = true;
}
}
GSwarm->SendMessage( NSwarm::FTimingMessage( NSwarm::PROGSTATE_Preparing4, ThreadIndex ) );
}
void FStaticLightingSystem::RadiosityIterationTextureMapping(FStaticLightingTextureMapping* TextureMapping, int32 PassIndex)
{
checkSlow(TextureMapping);
FStaticLightingMappingContext MappingContext(TextureMapping->Mesh,*this,&StartupDebugOutput);
LIGHTINGSTAT(FScopedRDTSCTimer CachingTime(MappingContext.Stats.RadiosityIterationThreadTime));
const FBoxSphereBounds3f ImportanceBounds = Scene.GetImportanceBounds();
FTexelToVertexMap TexelToVertexMap(TextureMapping->SurfaceCacheSizeX, TextureMapping->SurfaceCacheSizeY);
bool bDebugThisMapping = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
bDebugThisMapping = TextureMapping == Scene.DebugMapping;
int32 TexelDebugX = -1;
int32 TexelDebugY = -1;
if (bDebugThisMapping)
{
TexelDebugX = FMath::TruncToInt(Scene.DebugInput.LocalX / (float)TextureMapping->CachedSizeX * TextureMapping->SurfaceCacheSizeX);
TexelDebugY = FMath::TruncToInt(Scene.DebugInput.LocalY / (float)TextureMapping->CachedSizeY * TextureMapping->SurfaceCacheSizeY);
}
#endif
RasterizeToSurfaceCacheTextureMapping(TextureMapping, bDebugThisMapping, TexelToVertexMap);
if (ImportanceTracingSettings.bCacheFinalGatherHitPointsForRadiosity)
{
RadiosityIterationCachedHitpointsTextureMapping(TexelToVertexMap, TextureMapping, PassIndex);
}
else
{
const int32 SourceRadiosityBufferIndex = PassIndex % 2;
const int32 DestRadiosityBufferIndex = 1 - SourceRadiosityBufferIndex;
int32 NumAdaptiveRefinementLevels = PassIndex == 0 ? 1 : 0;
if (GeneralSettings.IndirectLightingQuality > 10)
{
NumAdaptiveRefinementLevels++;
}
const int32 RadiositySampleSet = FMath::Min<int32>(PassIndex, UE_ARRAY_COUNT(CachedHemisphereSamplesForRadiosity) - 1);
TLightingCache<FFinalGatherSample> RadiosityCache(TextureMapping->Mesh->BoundingBox, *this, 1);
FLMRandomStream RandomStream(0);
for (int32 Y = 0; Y < TextureMapping->SurfaceCacheSizeY; Y++)
{
for (int32 X = 0; X < TextureMapping->SurfaceCacheSizeX; X++)
{
bool bDebugThisTexel = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
if (bDebugThisMapping
&& Y == TexelDebugY
&& X == TexelDebugX)
{
bDebugThisTexel = true;
}
#endif
const FTexelToVertex& TexelToVertex = TexelToVertexMap(X,Y);
if (TexelToVertex.TotalSampleWeight > 0.0f)
{
FFullStaticLightingVertex Vertex = TexelToVertex.GetFullVertex();
Vertex.ApplyVertexModifications(TexelToVertex.ElementIndex, MaterialSettings.bUseNormalMapsForLighting, TextureMapping->Mesh);
FFinalGatherSample SkyLighting;
FFinalGatherSample UnusedSecondLighting;
float UnusedBackfacingHitsFraction;
if (!RadiosityCache.InterpolateLighting(Vertex, true, false, 1.0f, SkyLighting, UnusedSecondLighting, UnusedBackfacingHitsFraction, MappingContext.DebugCacheRecords))
{
FFinalGatherSample UniformSampledIncomingRadiance;
//@todo - find and pass in photons from the appropriate bounce number to improve bUseRadiositySolverForLightMultibounce quality
TArray<FVector4f> ImportancePhotonDirections;
FLightingCacheGatherInfo GatherInfo;
// Gather previous iteration results (double buffer)
EHemisphereGatherClassification GatherClassification = SourceRadiosityBufferIndex == 0 ? GLM_GatherRadiosityBuffer0 : GLM_GatherRadiosityBuffer1;
UniformSampledIncomingRadiance = IncomingRadianceAdaptive<FFinalGatherSample>(
TextureMapping,
Vertex,
TexelToVertex.SampleRadius,
false,
TexelToVertex.ElementIndex,
PassIndex + 3, /** BounceNumber */
RBM_ConstantNormalOffset,
GatherClassification,
NumAdaptiveRefinementLevels,
1.0f,
CachedHemisphereSamplesForRadiosity[RadiositySampleSet],
CachedHemisphereSamplesForRadiosityUniforms[RadiositySampleSet],
1,
ImportancePhotonDirections,
MappingContext,
RandomStream,
GatherInfo,
true, /* bGatheringForCachedDirectLighting */
false);
float OverrideRadius = 0;
TLightingCache<FFinalGatherSample>::FRecord<FFinalGatherSample> NewRecord(
Vertex,
TexelToVertex.ElementIndex,
GatherInfo,
TexelToVertex.SampleRadius,
OverrideRadius,
IrradianceCachingSettings,
GeneralSettings,
UniformSampledIncomingRadiance,
FVector4f(0, 0, 0, 0),
FVector4f(0, 0, 0, 0)
);
// Add the incident radiance sample to the cache.
RadiosityCache.AddRecord(NewRecord, false, false);
}
}
}
}
for (int32 Y = 0; Y < TextureMapping->SurfaceCacheSizeY; Y++)
{
for (int32 X = 0; X < TextureMapping->SurfaceCacheSizeX; X++)
{
bool bDebugThisTexel = false;
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
if (bDebugThisMapping
&& Y == TexelDebugY
&& X == TexelDebugX)
{
bDebugThisTexel = true;
}
#endif
const FTexelToVertex& TexelToVertex = TexelToVertexMap(X,Y);
if (TexelToVertex.TotalSampleWeight > 0.0f)
{
FFullStaticLightingVertex CurrentVertex = TexelToVertex.GetFullVertex();
CurrentVertex.ApplyVertexModifications(TexelToVertex.ElementIndex, MaterialSettings.bUseNormalMapsForLighting, TextureMapping->Mesh);
const int32 SurfaceCacheIndex = Y * TextureMapping->SurfaceCacheSizeX + X;
FFinalGatherSample IterationLighting;
FFinalGatherSample UnusedSecondLighting;
float UnusedBackfacingHitsFraction;
RadiosityCache.InterpolateLighting(CurrentVertex, false, false, IrradianceCachingSettings.SkyOcclusionSmoothnessReduction, IterationLighting, UnusedSecondLighting, UnusedBackfacingHitsFraction, MappingContext.DebugCacheRecords);
const bool bIsTranslucent = TextureMapping->Mesh->IsTranslucent(TexelToVertex.ElementIndex);
const FLinearColor Reflectance = (bIsTranslucent ? FLinearColor::Black : TextureMapping->Mesh->EvaluateTotalReflectance(CurrentVertex, TexelToVertex.ElementIndex)) * (float)INV_PI;
const FLinearColor IterationRadiosity = IterationLighting.IncidentLighting * Reflectance;
if (GeneralSettings.ViewSingleBounceNumber < 0 || GeneralSettings.ViewSingleBounceNumber == PassIndex + 2)
{
// Accumulate this bounce's lighting
TextureMapping->SurfaceCacheLighting[SurfaceCacheIndex] += IterationRadiosity;
}
// Store in one of the radiosity buffers for the next iteration
TextureMapping->RadiositySurfaceCache[DestRadiosityBufferIndex][SurfaceCacheIndex] = IterationRadiosity;
}
}
}
}
}
void FStaticLightingSystem::RadiosityIterationCachedHitpointsTextureMapping(const FTexelToVertexMap& TexelToVertexMap, FStaticLightingTextureMapping* TextureMapping, int32 PassIndex)
{
const int32 SourceRadiosityBufferIndex = PassIndex % 2;
const int32 DestRadiosityBufferIndex = 1 - SourceRadiosityBufferIndex;
FGatherHitPoints LocalGatherHitPoints;
if (bCompressRadiosityCachedData)
{
TextureMapping->CompressedGatherHitPoints.Decompress(LocalGatherHitPoints);
}
const FGatherHitPoints& GatherHitPoints = bCompressRadiosityCachedData ? LocalGatherHitPoints : TextureMapping->UncompressedGatherHitPoints;
TArray<FLinearColor> IterationRecordRadiosity;
IterationRecordRadiosity.Empty(GatherHitPoints.GatherHitPointRanges.Num());
IterationRecordRadiosity.AddUninitialized(GatherHitPoints.GatherHitPointRanges.Num());
for (int32 LightingCacheRecordIndex = 0; LightingCacheRecordIndex < GatherHitPoints.GatherHitPointRanges.Num(); LightingCacheRecordIndex++)
{
FLinearColor NewRadiosity = FLinearColor::Black;
const FArrayRange& HitPointRange = GatherHitPoints.GatherHitPointRanges[LightingCacheRecordIndex];
for (int32 HitPointIndex = HitPointRange.StartIndex; HitPointIndex < HitPointRange.StartIndex + HitPointRange.NumEntries; HitPointIndex++)
{
const FFinalGatherHitPoint& HitPoint = GatherHitPoints.GatherHitPointData[HitPointIndex];
const FStaticLightingMapping* HitTextureMapping = AllMappings[HitPoint.MappingIndex];
const FLinearColor IncomingRadiance = HitTextureMapping->GetCachedRadiosity(SourceRadiosityBufferIndex, HitPoint.MappingSurfaceCoordinate);
NewRadiosity += IncomingRadiance * HitPoint.Weight.GetFloat();
}
IterationRecordRadiosity[LightingCacheRecordIndex] = NewRadiosity;
}
FInfluencingRecords LocalInfluencingRecords;
if (bCompressRadiosityCachedData)
{
TextureMapping->CompressedInfluencingRecords.Decompress(LocalInfluencingRecords);
}
const FInfluencingRecords& InfluencingRecords = bCompressRadiosityCachedData ? LocalInfluencingRecords : TextureMapping->InfluencingRecordsSurfaceCache;
for (int32 Y = 0; Y < TextureMapping->SurfaceCacheSizeY; Y++)
{
for (int32 X = 0; X < TextureMapping->SurfaceCacheSizeX; X++)
{
const FTexelToVertex& TexelToVertex = TexelToVertexMap(X, Y);
if (TexelToVertex.TotalSampleWeight > 0.0f)
{
FFullStaticLightingVertex CurrentVertex = TexelToVertex.GetFullVertex();
CurrentVertex.ApplyVertexModifications(TexelToVertex.ElementIndex, MaterialSettings.bUseNormalMapsForLighting, TextureMapping->Mesh);
const int32 SurfaceCacheIndex = Y * TextureMapping->SurfaceCacheSizeX + X;
FArrayRange Range = InfluencingRecords.Ranges[SurfaceCacheIndex];
float TotalWeight = 0.0f;
FLinearColor AccumulatedRadiosity = FLinearColor::Black;
for (int32 InfluenceIndex = Range.StartIndex; InfluenceIndex < Range.StartIndex + Range.NumEntries; InfluenceIndex++)
{
FInfluencingRecord InfluencingRecord = InfluencingRecords.Data[InfluenceIndex];
float RecordWeight = InfluencingRecord.RecordWeight;
AccumulatedRadiosity += IterationRecordRadiosity[InfluencingRecord.RecordIndex] * RecordWeight;
TotalWeight += RecordWeight;
}
const bool bIsTranslucent = TextureMapping->Mesh->IsTranslucent(TexelToVertex.ElementIndex);
const FLinearColor DiffuseReflectance = (bIsTranslucent ? FLinearColor::Black : TextureMapping->Mesh->EvaluateTotalReflectance(CurrentVertex, TexelToVertex.ElementIndex)) * (float)INV_PI;
checkSlow(TotalWeight > 0.0f);
FLinearColor IterationRadiosity = (AccumulatedRadiosity / TotalWeight) * DiffuseReflectance;
if (GeneralSettings.ViewSingleBounceNumber < 0 || GeneralSettings.ViewSingleBounceNumber == PassIndex + 2)
{
// Accumulate this bounce's lighting
TextureMapping->SurfaceCacheLighting[SurfaceCacheIndex] += IterationRadiosity;
}
// Store in one of the radiosity buffers for the next iteration
TextureMapping->RadiositySurfaceCache[DestRadiosityBufferIndex][SurfaceCacheIndex] = IterationRadiosity;
}
}
}
}
size_t FStaticLightingMapping::FreeRadiosityTemporaries()
{
size_t RadiosityTemporariesSize = 0;
RadiosityTemporariesSize += RadiositySurfaceCache[0].GetAllocatedSize() + RadiositySurfaceCache[1].GetAllocatedSize();
RadiosityTemporariesSize += CompressedGatherHitPoints.GetAllocatedSize() + UncompressedGatherHitPoints.GetAllocatedSize();
RadiosityTemporariesSize += CompressedInfluencingRecords.GetAllocatedSize() + InfluencingRecordsSurfaceCache.GetAllocatedSize();
RadiositySurfaceCache[0].Empty();
RadiositySurfaceCache[1].Empty();
CompressedGatherHitPoints = FCompressedGatherHitPoints();
UncompressedGatherHitPoints = FGatherHitPoints();
CompressedInfluencingRecords = FCompressedInfluencingRecords();
InfluencingRecordsSurfaceCache = FInfluencingRecords();
return RadiosityTemporariesSize;
}
}