1.创建cardUpdateContext
2,。直接光照
3. 间接光照
context
1. 清除数据:ClearCardUpdateContextCS
/** * Batch clear all resources required for the subsequent card context update pass */ [numthreads(THREADGROUP_SIZE, 1, 1)] void ClearCardUpdateContextCS( uint3 DispatchThreadId : SV_DispatchThreadID) { uint ElementIndex = DispatchThreadId.x; if (ElementIndex < 1) { RWDirectLightingCardPageIndexAllocator[ElementIndex] = 0; RWIndirectLightingCardPageIndexAllocator[ElementIndex] = 0; } if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * MAX_UPDATE_BUCKET_STRIDE) { RWMaxUpdateBucket[ElementIndex] = 0; } if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * CARD_PAGE_TILE_ALLOCATOR_STRIDE) { RWCardPageTileAllocator[ElementIndex] = 0; } if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * PRIORITY_HISTOGRAM_SIZE) { RWPriorityHistogram[ElementIndex] = 0; } }
2. page更新直方图
BuildPageUpdatePriorityHistogram
[numthreads(THREADGROUP_SIZE, 1, 1)] void BuildPageUpdatePriorityHistogramCS( uint3 DispatchThreadId : SV_DispatchThreadID) { uint IndexInIndexBuffer = DispatchThreadId.x; if (IndexInIndexBuffer < CardPageNum) { uint CardPageIndex = IndexInIndexBuffer; FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex); FLumenCardData Card = GetLumenCardData(CardPage.CardIndex); const uint NumCardPageTiles = GetNumCardPageTiles(CardPage); if (NumCardPageTiles > 0) { BuildUpdatePriorityHistogram(Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_DIRECT_LIGHTING); BuildUpdatePriorityHistogram(Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING); } } }
优先级的计算
uint GetPriorityBucketIndex(FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint CardUpdateContext) { const float MaxUpdateFrequency = 7.0f; uint LastLightingUpdateFrameIndex = GetLastLightingUpdateFrameIndex(CardPage, CardUpdateContext); const float UpdateFactor = GetUpdateFactor(CardUpdateContext); // [1;N] uint FramesSinceLastUpdated = SurfaceCacheUpdateFrameIndex - LastLightingUpdateFrameIndex; // [0;MaxUpdateFrequency] float Frequency = 0.0f; { float DistanceFromViewer = 100000000.0f; for (uint ViewIndex = 0; ViewIndex < NumCameraOrigins; ViewIndex++) { float3 CardSpaceViewPosition = mul(WorldCameraOrigins[ViewIndex].xyz - Card.Origin, Card.WorldToLocalRotation); float3 CardPageLocalCenter; float3 CardPageLocalExtent; GetCardPageLocalBBox(CardPage, Card, CardPageLocalCenter, CardPageLocalExtent); DistanceFromViewer = min(DistanceFromViewer, sqrt(ComputeSquaredDistanceFromBoxToPoint(CardPageLocalCenter, CardPageLocalExtent, CardSpaceViewPosition))); } Frequency = MaxUpdateFrequency - clamp(DistanceFromViewer * FirstClipmapWorldExtentRcp, 0.0f, MaxUpdateFrequency); } // Drive frequency based on the feedback #if SURFACE_CACHE_FEEDBACK { const uint LastUsedFrameIndex = CardPageLastUsedBuffer[CardPageIndex]; const uint LastUsedHighResFrameIndex = CardPageHighResLastUsedBuffer[CardPageIndex]; const uint LastUsedCombinedFrameIndex = max(LastUsedFrameIndex, LastUsedHighResFrameIndex); FramesSinceLastUpdated = LastUsedCombinedFrameIndex > LastLightingUpdateFrameIndex ? LastUsedCombinedFrameIndex - LastLightingUpdateFrameIndex : 1; Frequency *= 0.5f; if (SurfaceCacheUpdateFrameIndex >= LastUsedHighResFrameIndex + 1) { Frequency += 0.5f * MaxUpdateFrequency * saturate((SurfaceCacheUpdateFrameIndex - (LastUsedHighResFrameIndex + 1)) / 2.0f); } } #endif uint BucketIndex = 0; if (LastLightingUpdateFrameIndex == 0) { // Special case where page wasn't ever updated, just place into first 8 most important buckets based on the frequency BucketIndex = clamp(MaxUpdateFrequency - Frequency, 0.0f, MaxUpdateFrequency); } else { // [0;N] float UpdateImportance = FramesSinceLastUpdated * (Frequency + 1.0f); // Offset from [1;N] and invert in order to place most important pages in bucket 0 BucketIndex = PRIORITY_HISTOGRAM_SIZE - 1 - clamp(log2(UpdateImportance / (MaxUpdateFrequency + 1.0f)), 0, PRIORITY_HISTOGRAM_SIZE - 1); } return BucketIndex; } void BuildUpdatePriorityHistogram(FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint NumCardPageTiles, uint CardUpdateContext) { uint PriorityBucketIndex = GetPriorityBucketIndex(Card, CardPage, CardPageIndex, CardUpdateContext); InterlockedAdd(RWPriorityHistogram[CardUpdateContext * PRIORITY_HISTOGRAM_SIZE + PriorityBucketIndex], NumCardPageTiles); }
3.最大优先级的找到
SelectMaxUpdateBucketCS
void SelectMaxUpdateBucket(uint CardUpdateContext) { const uint MaxTilesToUpdate = GetMaxTilesToUpdate(CardUpdateContext); uint UpdateTileSum = 0; uint PriorityBucketIndex = 0; uint PriorityBucketMaxTiles = MaxTilesToUpdate; for (; PriorityBucketIndex < PRIORITY_HISTOGRAM_SIZE; ++PriorityBucketIndex) { uint TilesPerBucket = PriorityHistogram[CardUpdateContext * PRIORITY_HISTOGRAM_SIZE + PriorityBucketIndex]; if (UpdateTileSum + TilesPerBucket >= MaxTilesToUpdate) { PriorityBucketMaxTiles = MaxTilesToUpdate - UpdateTileSum; break; } UpdateTileSum += TilesPerBucket; } RWMaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 0] = PriorityBucketIndex; RWMaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 1] = PriorityBucketMaxTiles; } /** * Compute max bucket histogram to update and how many tiles should be updated in that last bucket */ [numthreads(THREADGROUP_SIZE, 1, 1)] void SelectMaxUpdateBucketCS( uint3 GroupId : SV_GroupID, uint3 GroupThreadId : SV_GroupThreadID, uint3 DispatchThreadId : SV_DispatchThreadID) { if (GroupId.x == 0 && GroupThreadId.x == 0) { SelectMaxUpdateBucket(CARD_UPDATE_CONTEXT_DIRECT_LIGHTING); } else if (GroupId.x == 1 && GroupThreadId.x == 0) { SelectMaxUpdateBucket(CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING); } }
4.创建更新列表
BuildCardsUpdateListCS:
card更新列表得到,设置更新数据:再次遍历每个 Card Page,根据最大可更新的 Bucket 将当前 Card Page Index 加入到更新列表中,对应 RDG 资源名为 Lumen.DirectLightingCardPageIndexData 和 Lumen.IndirectLightingCardPageIndexData。同时每个加入到更新列表中的 Card Page 记录当前帧索引,用于下次构建直方图,Pass:Build cards update list。
/** * Iterate over all cards and pick first N for update based on the histogram max update bucket */ [numthreads(THREADGROUP_SIZE, 1, 1)] void BuildCardsUpdateListCS( uint3 DispatchThreadId : SV_DispatchThreadID) { uint IndexInIndexBuffer = DispatchThreadId.x; if (IndexInIndexBuffer < CardPageNum) { const uint CardPageIndex = IndexInIndexBuffer; FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex); FLumenCardData Card = GetLumenCardData(CardPage.CardIndex); const uint NumCardPageTiles = GetNumCardPageTiles(CardPage); if (NumCardPageTiles > 0) { bool bUpdatedCardPage = false; // On radiosity atlas reset invalidate all history data if (IndirectLightingHistoryValid == 0) { CardPage.LastIndirectLightingUpdateFrameIndex = 0; bUpdatedCardPage = true; } if (BuildCardsUpdateList( Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_DIRECT_LIGHTING, RWDirectLightingCardPageIndexAllocator, RWDirectLightingCardPageIndexData)) { CardPage.LastDirectLightingUpdateFrameIndex = SurfaceCacheUpdateFrameIndex; bUpdatedCardPage = true; } if (BuildCardsUpdateList( Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING, RWIndirectLightingCardPageIndexAllocator, RWIndirectLightingCardPageIndexData)) { CardPage.LastIndirectLightingUpdateFrameIndex = SurfaceCacheUpdateFrameIndex; CardPage.IndirectLightingTemporalIndex = CardPage.IndirectLightingTemporalIndex + 1; bUpdatedCardPage = true; } if (bUpdatedCardPage && FreezeUpdateFrame == 0) { SetCardPageUpdateData(CardPageIndex, CardPage); } } } }
BuildCardsUpdateList
bool BuildCardsUpdateList( FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint NumCardPageTiles, uint CardUpdateContext, RWStructuredBuffer<uint> RWCardPageIndexAllocator, RWStructuredBuffer<uint> RWCardPageIndexData) { const uint MaxTilesToUpdate = GetMaxTilesToUpdate(CardUpdateContext); const uint MaxUpdateBucketIndex = MaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 0]; const uint MaxUpdateBucketMaxTiles = MaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 1]; // Update everything up to the max selected priority bucket uint PriorityBucketIndex = GetPriorityBucketIndex(Card, CardPage, CardPageIndex, CardUpdateContext); bool bUpdateThisPage = PriorityBucketIndex <= MaxUpdateBucketIndex; if (bUpdateThisPage && PriorityBucketIndex == MaxUpdateBucketIndex) { // Can't update more than MaxUpdateBucketMaxTiles in the max bucket to preserve the general order uint NumAllocatedTilesInMaxUpdateBucket = 0; InterlockedAdd(RWCardPageTileAllocator[CARD_PAGE_TILE_ALLOCATOR_STRIDE * CardUpdateContext + 1], NumCardPageTiles, NumAllocatedTilesInMaxUpdateBucket); if (!(NumAllocatedTilesInMaxUpdateBucket + NumCardPageTiles <= MaxUpdateBucketMaxTiles)) { bUpdateThisPage = false; } } if (bUpdateThisPage) { bUpdateThisPage = false; uint NumAllocatedTiles = 0; InterlockedAdd(RWCardPageTileAllocator[CARD_PAGE_TILE_ALLOCATOR_STRIDE * CardUpdateContext + 0], NumCardPageTiles, NumAllocatedTiles); if (NumAllocatedTiles + NumCardPageTiles <= MaxTilesToUpdate) { uint NextIndex = 0; InterlockedAdd(RWCardPageIndexAllocator[0], 1, NextIndex); if (NextIndex < CardPageNum) { RWCardPageIndexData[NextIndex] = CardPageIndex; bUpdateThisPage = true; } } } return bUpdateThisPage; }
设置数据:
// Store only card page update data void SetCardPageUpdateData(uint CardPageId, FLumenCardPageData CardPageData) { // Note: layout must match FLumenCardPageData in C++ uint4 Vector4; Vector4.x = CardPageData.LastDirectLightingUpdateFrameIndex; Vector4.y = CardPageData.LastIndirectLightingUpdateFrameIndex; Vector4.z = CardPageData.IndirectLightingTemporalIndex; Vector4.w = 0; uint BaseOffset = CardPageId * LUMEN_CARD_PAGE_DATA_STRIDE; RWLumenCardPageDataBuffer[BaseOffset + 4] = asfloat(Vector4); }
5.设置命令参数
设置间接参数FSetCardPageIndexIndirectArgsCS:根据上面的数据生成后续步骤的 Indirect Draw 和 Indirect Dispatch 参数
{ FSetCardPageIndexIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSetCardPageIndexIndirectArgsCS::FParameters>(); PassParameters->RWDirectLightingDrawCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs); PassParameters->RWDirectLightingDispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(DirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs); PassParameters->RWIndirectLightingDrawCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.DrawCardPageIndicesIndirectArgs); PassParameters->RWIndirectLightingDispatchCardPageIndicesIndirectArgs = GraphBuilder.CreateUAV(IndirectLightingCardUpdateContext.DispatchCardPageIndicesIndirectArgs); PassParameters->DirectLightingCardPageIndexAllocator = DirectCardPageIndexAllocatorSRV; PassParameters->IndirectLightingCardPageIndexAllocator = IndirectCardPageIndexAllocatorSRV; PassParameters->VertexCountPerInstanceIndirect = GRHISupportsRectTopology ? 3 : 6; auto ComputeShader = Views[0].ShaderMap->GetShader<FSetCardPageIndexIndirectArgsCS>(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("SetCardPageIndexIndirectArgs"), ComputePassFlags, ComputeShader, PassParameters, FIntVector(1, 1, 1)); }
FSetCardPageIndexIndirectArgsCS
RWBuffer<uint> RWDirectLightingDrawCardPageIndicesIndirectArgs; RWBuffer<uint> RWDirectLightingDispatchCardPageIndicesIndirectArgs; RWBuffer<uint> RWIndirectLightingDrawCardPageIndicesIndirectArgs; RWBuffer<uint> RWIndirectLightingDispatchCardPageIndicesIndirectArgs; StructuredBuffer<uint> DirectLightingCardPageIndexAllocator; StructuredBuffer<uint> IndirectLightingCardPageIndexAllocator; uint VertexCountPerInstanceIndirect; [numthreads(THREADGROUP_SIZE, 1, 1)] void SetCardPageIndexIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID) { if (DispatchThreadId.x == 0) { { uint NumPageIndices = DirectLightingCardPageIndexAllocator[0]; // FRHIDrawIndirectParameters RWDirectLightingDrawCardPageIndicesIndirectArgs[0] = VertexCountPerInstanceIndirect; RWDirectLightingDrawCardPageIndicesIndirectArgs[1] = NumPageIndices; RWDirectLightingDrawCardPageIndicesIndirectArgs[2] = 0; RWDirectLightingDrawCardPageIndicesIndirectArgs[3] = 0; // Thread per page WriteDispatchIndirectArgs(RWDirectLightingDispatchCardPageIndicesIndirectArgs, 0, (NumPageIndices + 63) / 64, 1, 1); // Thread per tile WriteDispatchIndirectArgs(RWDirectLightingDispatchCardPageIndicesIndirectArgs, 1, 4 * NumPageIndices, 1, 1); } { uint NumPageIndices = IndirectLightingCardPageIndexAllocator[0]; // FRHIDrawIndirectParameters RWIndirectLightingDrawCardPageIndicesIndirectArgs[0] = VertexCountPerInstanceIndirect; RWIndirectLightingDrawCardPageIndicesIndirectArgs[1] = NumPageIndices; RWIndirectLightingDrawCardPageIndicesIndirectArgs[2] = 0; RWIndirectLightingDrawCardPageIndicesIndirectArgs[3] = 0; // Thread per page WriteDispatchIndirectArgs(RWIndirectLightingDispatchCardPageIndicesIndirectArgs, 0, (NumPageIndices + 63) / 64, 1, 1); // Thread per tile WriteDispatchIndirectArgs(RWIndirectLightingDispatchCardPageIndicesIndirectArgs, 1, 4 * NumPageIndices, 1, 1); } } }
RenderDirectLightingForLumenScene
void FDeferredShadingSceneRenderer::RenderDirectLightingForLumenScene(FRDGBuilder& GraphBuilder,
const FLumenSceneFrameTemporaries& FrameTemporaries,
const FLumenDirectLightingTaskData* LightingTaskData,
const FLumenCardUpdateContext& CardUpdateContext,
ERDGPassFlags ComputePassFlags)
CullDirectLightingTiles:将cardPage分为tile,以tile为单位对光源进行Culling;每个光源一个light列表
SpliceCardPagesIntoTiles
FBuildLightTilesCS:创建light tile列表
FComputeLightTileOffsetsPerLightCS:计算每个tile的偏移
FInitializeLightTileIndirectArgsCS:为剔除tile初始化间接参数
FCompactLightTilesCS:压缩card tile
ComputeShadowMaskFromLightAttenuation:
FLumenDirectLightingShadowMaskFromLightAttenuationCS 光照函数或普通的
FInitShadowTraceIndirectArgsCS
TraceDistanceFieldShadows
CullMeshObjectsForLightCards
CullDistanceFieldObjectsForLight
ScatterObjectsToShadowTiles
RenderDirectLightIntoLumenCardsBatched:应用光照
CombineLumenSceneLighting:更新最终light
标签:CardPageIndex,CardUpdateContext,CardPage,uint,光照,lumen,const,Card From: https://www.cnblogs.com/Shaojunping/p/18357502