Files
UnrealEngine/Engine/Shaders/Private/Lumen/LumenSceneLighting.usf
2025-05-18 13:04:45 +08:00

668 lines
24 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LumenCardSceneLighting.usf
=============================================================================*/
#include "../Common.ush"
#include "../BRDF.ush"
#include "LumenFloatQuantization.ush"
#include "LumenCardCommon.ush"
#include "LumenCardTile.ush"
#include "LumenCardTileShadowDownsampleFactor.ush"
#include "LumenSceneLighting.ush"
#include "SurfaceCache/LumenSurfaceCacheSampling.ush"
#include "Radiosity/LumenRadiosity.ush"
#ifndef THREADGROUP_SIZE
#define THREADGROUP_SIZE 1
#endif
RWStructuredBuffer<float4> RWCardPageBuffer;
StructuredBuffer<uint> CardPageLastUsedBuffer;
StructuredBuffer<uint> CardPageHighResLastUsedBuffer;
RWStructuredBuffer<uint> RWDirectLightingCardPageIndexAllocator;
RWStructuredBuffer<uint> RWDirectLightingCardPageIndexData;
RWStructuredBuffer<uint> RWIndirectLightingCardPageIndexAllocator;
RWStructuredBuffer<uint> RWIndirectLightingCardPageIndexData;
RWStructuredBuffer<uint> RWPriorityHistogram;
StructuredBuffer<uint> PriorityHistogram;
RWStructuredBuffer<uint> RWMaxUpdateBucket;
StructuredBuffer<uint> MaxUpdateBucket;
RWStructuredBuffer<uint> RWCardPageTileAllocator;
StructuredBuffer<uint> CardPageTileAllocator;
float MaxDistanceFromFrustumToPrioritize;
uint FreezeUpdateFrame;
uint CardPageNum;
uint MaxDirectLightingTilesToUpdate;
uint MaxIndirectLightingTilesToUpdate;
float FirstClipmapWorldExtentRcp;
uint IndirectLightingHistoryValid;
uint NumCameraOrigins;
float4 WorldCameraOrigins[LUMEN_MAX_VIEWS];
float4 CameraFrustumPlanes[LUMEN_MAX_VIEWS * LUMEN_MAX_CAMERA_PLANES];
float DirectLightingUpdateFactor;
float IndirectLightingUpdateFactor;
/**
* 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;
}
}
uint GetMaxTilesToUpdate(uint CardUpdateContext)
{
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? MaxDirectLightingTilesToUpdate : MaxIndirectLightingTilesToUpdate;
}
uint GetLastLightingUpdateFrameIndex(FLumenCardPageData CardPage, uint CardUpdateContext)
{
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? CardPage.LastDirectLightingUpdateFrameIndex : CardPage.LastIndirectLightingUpdateFrameIndex;
}
uint GetUpdateFactor(uint CardUpdateContext)
{
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? DirectLightingUpdateFactor : IndirectLightingUpdateFactor;
}
uint GetNumCardPageTiles(FLumenCardPageData CardPage)
{
return (CardPage.SizeInTexels.x * CardPage.SizeInTexels.y) / (CARD_TILE_SIZE * CARD_TILE_SIZE);
}
uint GetPriorityBucketIndex(FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint CardUpdateContext)
{
uint LastLightingUpdateFrameIndex = GetLastLightingUpdateFrameIndex(CardPage, CardUpdateContext);
const float UpdateFactor = GetUpdateFactor(CardUpdateContext);
// FramesSinceLastUpdated >= 1
uint FramesSinceLastUpdated = SurfaceCacheUpdateFrameIndex - LastLightingUpdateFrameIndex;
float UpdateSpeed = 1.0f;
{
bool bNearAnyFrustum = false;
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)));
// Check whether it's near frustum
float3 CardPageWorldCenter = mul(Card.WorldToLocalRotation, CardPageLocalCenter) + DFFastToTranslatedWorld(Card.Origin, GetPreViewTranslation(ViewIndex));
float3 CardPageWorldExtent = mul(abs(Card.WorldToLocalRotation), CardPageLocalExtent);
if (MaxDistanceFromFrustumToPrioritize >= 0.0f)
{
bool bNearFrustum = true;
for (uint PlaneIndex = 0; PlaneIndex < LUMEN_MAX_CAMERA_PLANES; ++PlaneIndex)
{
float DistanceFromPageCenterToPlane = dot(CameraFrustumPlanes[LUMEN_MAX_CAMERA_PLANES * ViewIndex + PlaneIndex], float4(CardPageWorldCenter, 1.0f));
float Radius = dot(CardPageWorldExtent, abs(CameraFrustumPlanes[LUMEN_MAX_CAMERA_PLANES * ViewIndex + PlaneIndex].xyz));
bNearFrustum = bNearFrustum && (DistanceFromPageCenterToPlane <= Radius + MaxDistanceFromFrustumToPrioritize);
}
if (bNearFrustum)
{
bNearAnyFrustum = true;
}
}
}
UpdateSpeed = 1.0f / (1.0f + max(DistanceFromViewer * FirstClipmapWorldExtentRcp, 0.0f));
// Speedup updates for card pages near frustum
if (bNearAnyFrustum)
{
UpdateSpeed *= 2.0f;
}
}
// Update speed based on the feedback
#if SURFACE_CACHE_FEEDBACK
{
const uint LastUsedHighResFrameIndex = CardPageHighResLastUsedBuffer[CardPageIndex];
// Override update rate for cards used recently
if (LastUsedHighResFrameIndex + 2 >= SurfaceCacheUpdateFrameIndex && LastUsedHighResFrameIndex <= SurfaceCacheUpdateFrameIndex)
{
UpdateSpeed = 2.0f;
}
}
#endif
if (LastLightingUpdateFrameIndex == 0)
{
// Page wasn't ever updated
FramesSinceLastUpdated = 2048;
}
// Map desired page update speed to buckets and place most important pages in bucket 0
uint BucketIndex = PRIORITY_HISTOGRAM_SIZE - 1 - clamp(log2(max(4.0f * FramesSinceLastUpdated * UpdateSpeed, 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);
}
/**
* Iterate over all pages and build a histogram of card update priorities
*/
[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);
}
}
}
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);
}
}
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;
}
/**
* 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;
CardPage.DirectLightingTemporalIndex += 1;
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);
}
}
}
}
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, DivideAndRoundUp64(NumPageIndices), 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, DivideAndRoundUp64(NumPageIndices), 1, 1);
// Thread per tile
WriteDispatchIndirectArgs(RWIndirectLightingDispatchCardPageIndicesIndirectArgs, 1, 4 * NumPageIndices, 1, 1);
}
}
}
RWStructuredBuffer<uint> RWQuadAllocator;
RWStructuredBuffer<uint> RWQuadData;
StructuredBuffer<uint> CardPageIndexAllocator;
StructuredBuffer<uint> CardPageIndexData;
float2 IndirectLightingAtlasSize;
void RasterizeToCardsVS(
uint VertexId : SV_VertexID,
uint InstanceId : SV_InstanceID,
out FCardVSToPS CardInterpolants,
out float4 OutPosition : SV_POSITION
)
{
float2 TexCoord = float2(0.0f, 0.0f);
TexCoord.x += VertexId == 1 || VertexId == 2 || VertexId == 4 ? 1.0f : 0.0f;
TexCoord.y += VertexId == 2 || VertexId == 4 || VertexId == 5 ? 1.0f : 0.0f;
uint QuadIndex = InstanceId.x;
CardInterpolants = (FCardVSToPS)0;
OutPosition = 0;
if (QuadIndex < CardPageIndexAllocator[0])
{
uint CardPageIndex = CardPageIndexData[QuadIndex];
FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex);
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
float2 ScreenUV = lerp(CardPage.PhysicalAtlasUVRect.xy, CardPage.PhysicalAtlasUVRect.zw, TexCoord);
float2 AtlasUV = ScreenUV;
#if RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR == 1
float2 IndirectLightingAtlasUV = AtlasUV;
#else
// When sampling from a downsampled Indirect Lighting atlas we need to appropriately clamp input UVs to prevent bilinear reading outside of the valid area
float2 CardWidthInTexels = (CardPage.PhysicalAtlasUVRect.zw - CardPage.PhysicalAtlasUVRect.xy) * IndirectLightingAtlasSize;
float2 ClampBorder = 0.5f / CardWidthInTexels;
float2 IndirectLightingTexCoord = clamp(TexCoord, ClampBorder, 1.0f - ClampBorder);
float2 IndirectLightingAtlasUV = lerp(CardPage.PhysicalAtlasUVRect.xy, CardPage.PhysicalAtlasUVRect.zw, IndirectLightingTexCoord);
#endif
float2 ScreenPosition = float2(2.0f, -2.0f) * ScreenUV + float2(-1.0f, 1.0f);
OutPosition = float4(ScreenPosition, 0, 1);
float2 QuadCorner = -2.0f * TexCoord + 1.0f;
CardInterpolants.AtlasUV = AtlasUV;
CardInterpolants.IndirectLightingAtlasUV = IndirectLightingAtlasUV;
CardInterpolants.CardUV = lerp(CardPage.CardUVRect.xy, CardPage.CardUVRect.zw, TexCoord);
CardInterpolants.CardTileIndex = 0;
CardInterpolants.CardPageIndex = CardPageIndex;
}
}
SamplerState BilinearClampedSampler;
StructuredBuffer<uint> CardTiles;
RWTexture2D<float3> RWFinalLightingAtlas;
float2 IndirectLightingAtlasHalfTexelSize;
[numthreads(CARD_TILE_SIZE, CARD_TILE_SIZE, 1)]
void CombineLumenSceneLightingCS(
uint3 GroupId : SV_GroupID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint CardTileIndex = GroupId.x;
uint2 TexelCoordInTile = GroupThreadId.xy;
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
uint2 CoordInCardPage = CARD_TILE_SIZE * CardTile.TileCoord + TexelCoordInTile;
uint2 AtlasCoord = CardPage.PhysicalAtlasCoord + CoordInCardPage;
float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (CoordInCardPage + 0.5);
#if RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR == 1
float2 IndirectLightingAtlasUV = AtlasUV;
#else
// When sampling from a downsampled Indirect Lighting atlas we need to appropriately clamp input UVs to prevent bilinear reading outside of the valid area
float2 IndirectLightingAtlasUV = clamp(AtlasUV, CardPage.PhysicalAtlasUVRect.xy + IndirectLightingAtlasHalfTexelSize, CardPage.PhysicalAtlasUVRect.zw - IndirectLightingAtlasHalfTexelSize);
#endif
float3 Albedo = Texture2DSampleLevel(AlbedoAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
float3 Emissive = Texture2DSampleLevel(EmissiveAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
float3 DirectLighting = Texture2DSampleLevel(DirectLightingAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
float3 IndirectLighting = Texture2DSampleLevel(IndirectLightingAtlas, BilinearClampedSampler, IndirectLightingAtlasUV, 0).xyz;
float3 FinalLighting = CombineFinalLighting(Albedo, Emissive, DirectLighting, IndirectLighting);
RWFinalLightingAtlas[AtlasCoord] = QuantizeForFloatRenderTarget(FinalLighting, int3(AtlasCoord, View.StateFrameIndexMod8 + 2));
}
void ClearLumenCardsPS(
out float4 Target0 : SV_Target0
#if NUM_TARGETS > 1
,out float4 Target1 : SV_Target1
#endif
)
{
Target0 = float4(0.0f, 0.0f, 0.0f, 0.0f);
#if NUM_TARGETS > 1
Target1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
#endif
}
void ClearLumenCardCapturePS(
out float4 OutAlbedo : SV_Target0,
out float4 OutNormals : SV_Target1,
out float4 OutEmissive : SV_Target2
)
{
OutAlbedo = float4(0.0f, 0.0f, 0.0f, 0.0f);
OutNormals = float4(0.5f, 0.5f, 0.0f, 0.0f);
OutEmissive = float4(0.0f, 0.0f, 0.0f, 0.0f);
}
#ifdef ResampleLightingHistoryToCardCaptureAtlasCS
#define RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR (FEATURE_LEVEL >= FEATURE_LEVEL_SM6 || PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS)
Buffer<uint> NewCardTileResampleData;
Buffer<uint4> NewCardPageResampleData;
Buffer<uint4> RectCoordBuffer;
Texture2D RadiosityNumFramesAccumulatedAtlas;
RWTexture2D<float4> RWDirectLightingCardCaptureAtlas;
RWTexture2D<float4> RWRadiosityCardCaptureAtlas;
RWTexture2D<UNORM float> RWRadiosityNumFramesAccumulatedCardCaptureAtlas;
uint CardCaptureAtlasWidthInTiles;
[numthreads(CARD_TILE_SIZE, CARD_TILE_SIZE, 1)]
void ResampleLightingHistoryToCardCaptureAtlasCS(uint3 GroupId : SV_GroupID, uint3 GroupThreadId : SV_GroupThreadID, uint GroupThreadIndex : SV_GroupIndex)
{
const uint TileIndex = GroupId.x;
const uint PackedTileData = NewCardTileResampleData[TileIndex];
const uint2 TileCoordInRect = uint2(BitFieldExtractU32(PackedTileData, 4, 0), BitFieldExtractU32(PackedTileData, 4, 4));
const uint RectIndex = BitFieldExtractU32(PackedTileData, 24, 8);
const uint4 RectCoord = RectCoordBuffer[RectIndex];
const uint2 TexelCoordInRect = TileCoordInRect * CARD_TILE_SIZE + GroupThreadId.xy;
const uint2 RectSize = RectCoord.zw - RectCoord.xy;
const float2 RectUV = (TexelCoordInRect + float2(0.5, 0.5)) / RectSize;
float3 OutDirectLightingCardCaptureAtlas = 0;
float3 OutRadiosityCardCaptureAtlas = 0;
float OutRadiosityNumFramesAccumulatedCardCaptureAtlas = 0;
uint4 ShadowDownsampleFactorLow = 0;
uint4 ShadowDownsampleFactorHigh = 0;
uint CardIndex = NewCardPageResampleData[RectIndex * 2 + 0].x;
if (CardIndex != LUMEN_INVALID_CARD_INDEX)
{
// Load new card attributes manually
float4 NewCardUVRect = asfloat(NewCardPageResampleData[RectIndex * 2 + 1]);
float2 NewCardUV = RectUV * (NewCardUVRect.zw - NewCardUVRect.xy) + NewCardUVRect.xy;
// LumenCardScene contains the old card structure during the resample
FLumenCardData OldCard = GetLumenCardData(CardIndex);
// Assuming card extent hasn't changed
float2 LocalSamplePosition = GetCardLocalPosition(OldCard.LocalExtent, NewCardUV, 0.0f).xy;
FLumenCardSample CardSample = ComputeSurfaceCacheSample(OldCard, CardIndex, LocalSamplePosition, 0.0f, true);
if (CardSample.bValid)
{
OutDirectLightingCardCaptureAtlas = Texture2DSampleLevel(DirectLightingAtlas, GlobalBilinearClampedSampler, CardSample.PhysicalAtlasUV, 0.0f).xyz;
OutRadiosityCardCaptureAtlas = Texture2DSampleLevel(IndirectLightingAtlas, GlobalBilinearClampedSampler, CardSample.IndirectLightingPhysicalAtlasUV, 0.0f).xyz;
OutRadiosityNumFramesAccumulatedCardCaptureAtlas = Texture2DSampleLevel(RadiosityNumFramesAccumulatedAtlas, GlobalBilinearClampedSampler, CardSample.IndirectLightingPhysicalAtlasUV, 0.0f).x;
#if RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR
const uint2 TileCoord = CardSample.PhysicalAtlasUV * LumenCardScene.PhysicalAtlasSize / CARD_TILE_SIZE;
const uint PhysicalAtlasWidthInTiles = LumenCardScene.PhysicalAtlasSize.x / CARD_TILE_SIZE;
LoadLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, TileCoord, PhysicalAtlasWidthInTiles);
#endif
}
}
const uint2 WriteTexelCoord = RectCoord.xy + TexelCoordInRect;
const uint2 WriteTileCoord = WriteTexelCoord / CARD_TILE_SIZE;
RWDirectLightingCardCaptureAtlas[WriteTexelCoord] = float4(OutDirectLightingCardCaptureAtlas, 0.f);
RWRadiosityCardCaptureAtlas[WriteTexelCoord] = float4(OutRadiosityCardCaptureAtlas, 0.f);
RWRadiosityNumFramesAccumulatedCardCaptureAtlas[WriteTexelCoord] = OutRadiosityNumFramesAccumulatedCardCaptureAtlas;
#if RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR
ShadowDownsampleFactorLow = WaveActiveBitAnd(ShadowDownsampleFactorLow);
ShadowDownsampleFactorHigh = WaveActiveBitAnd(ShadowDownsampleFactorHigh);
#endif
if (GroupThreadIndex == 0)
{
WriteLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, WriteTileCoord, CardCaptureAtlasWidthInTiles);
}
}
#endif
Texture2D AlbedoCardCaptureAtlas;
Texture2D EmissiveCardCaptureAtlas;
Texture2D DirectLightingCardCaptureAtlas;
Texture2D RadiosityCardCaptureAtlas;
Texture2D RadiosityNumFramesAccumulatedCardCaptureAtlas;
uint2 CardCaptureAtlasSizeInTiles;
uint OutputAtlasWidthInTiles;
void CopyCardCaptureLightingToAtlasPS(
float4 SvPosition : SV_POSITION,
float2 AtlasUV : TEXCOORD0,
out float3 OutDirectLightingAtlas : SV_Target0,
out float3 OutFinalLightingAtlas : SV_Target1
#if INDIRECT_LIGHTING
, out float3 OutRadiosityAtlas : SV_Target2
, out float OutRadiosityNumFramesAccumulatedAtlas : SV_Target3
#endif
)
{
float3 Albedo = 0.0f;
float3 Emissive = 0.0f;
float3 DirectLighting = 0.0f;
float3 IndirectLighting = 0.0f;
float RadiosityNumFramesAccumulated = 0.0f;
uint4 ShadowDownsampleFactorLow = 0;
uint4 ShadowDownsampleFactorHigh = 0;
#if RESAMPLE
{
Albedo = Texture2DSampleLevel(AlbedoCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
Emissive = Texture2DSampleLevel(EmissiveCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
DirectLighting = Texture2DSampleLevel(DirectLightingCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
IndirectLighting = Texture2DSampleLevel(RadiosityCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
RadiosityNumFramesAccumulated = Texture2DSampleLevel(RadiosityNumFramesAccumulatedCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).x;
const uint2 TileCoord = AtlasUV * CardCaptureAtlasSizeInTiles;
LoadLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, TileCoord, CardCaptureAtlasSizeInTiles.x);
}
#endif
OutDirectLightingAtlas = DirectLighting;
OutFinalLightingAtlas = CombineFinalLighting(Albedo, Emissive, DirectLighting, IndirectLighting);
#if INDIRECT_LIGHTING
{
OutRadiosityAtlas = IndirectLighting;
OutRadiosityNumFramesAccumulatedAtlas = RadiosityNumFramesAccumulated;
}
#endif
WriteLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, SvPosition.xy / CARD_TILE_SIZE, OutputAtlasWidthInTiles);
}