Add support for using shadow maps from linked scene rendering (eg. for 1p weapons)

This commit is contained in:
Wojtek Figat
2025-07-25 19:59:58 +02:00
parent 6132e45e25
commit f37b75df7b
3 changed files with 75 additions and 20 deletions

View File

@@ -175,6 +175,12 @@ public:
return (const T*)FindCustomBuffer(name, withLinked);
}
template<class T>
const T* FindLinkedBuffer(const StringView& name) const
{
return LinkedCustomBuffers ? (const T*)LinkedCustomBuffers->FindCustomBuffer(name, true) : nullptr;
}
template<class T>
T* GetCustomBuffer(const StringView& name, bool withLinked = true)
{

View File

@@ -69,6 +69,7 @@ struct ShadowAtlasLightTile
{
ShadowsAtlasRectTile* RectTile;
ShadowsAtlasRectTile* StaticRectTile;
const ShadowsAtlasRectTile* LinkedRectTile;
Matrix WorldToShadow;
float FramesToUpdate; // Amount of frames (with fraction) until the next shadow update can happen
bool SkipUpdate;
@@ -94,6 +95,7 @@ struct ShadowAtlasLightTile
void ClearStatic()
{
StaticRectTile = nullptr;
LinkedRectTile = nullptr;
FramesToUpdate = 0;
SkipUpdate = false;
}
@@ -301,6 +303,7 @@ public:
GPUTexture* StaticShadowMapAtlas = nullptr;
DynamicTypedBuffer ShadowsBuffer;
GPUBufferView* ShadowsBufferView = nullptr;
const ShadowsCustomBuffer* LinkedShadows = nullptr;
RectPackAtlas<ShadowsAtlasRectTile> Atlas;
RectPackAtlas<ShadowsAtlasRectTile> StaticAtlas;
Dictionary<Guid, ShadowAtlasLight> Lights;
@@ -1046,6 +1049,32 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
}
}
void ShadowsPass::ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData) const
{
// Color.r is used by PS_DepthClear in Quad shader to clear depth
quadShaderData.Color = Float4::One;
context->UpdateCB(quadShaderCB, &quadShaderData);
context->BindCB(0, quadShaderCB);
// Clear tile depth
context->SetState(_psDepthClear);
context->DrawFullscreenTriangle();
}
void ShadowsPass::CopyShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData, const GPUTexture* srcShadowMap, const ShadowsAtlasRectTile* srcTile) const
{
// Color.xyzw is used by PS_DepthCopy in Quad shader to scale input texture UVs
const float staticAtlasResolutionInv = 1.0f / (float)srcShadowMap->Width();
quadShaderData.Color = Float4(srcTile->Width, srcTile->Height, srcTile->X, srcTile->Y) * staticAtlasResolutionInv;
context->UpdateCB(quadShaderCB, &quadShaderData);
context->BindCB(0, quadShaderCB);
// Copy tile depth
context->BindSR(0, srcShadowMap->View());
context->SetState(_psDepthCopy);
context->DrawFullscreenTriangle();
}
void ShadowsPass::Dispose()
{
// Base
@@ -1102,11 +1131,14 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
// Initialize shadow atlas
auto& shadows = *renderContext.Buffers->GetCustomBuffer<ShadowsCustomBuffer>(TEXT("Shadows"), false);
shadows.LinkedShadows = renderContext.Buffers->FindLinkedBuffer<ShadowsCustomBuffer>(TEXT("Shadows"));
if (shadows.LinkedShadows && (shadows.LinkedShadows->LastFrameUsed != currentFrame || shadows.LinkedShadows->ViewOrigin != renderContext.View.Origin))
shadows.LinkedShadows = nullptr; // Don't use incompatible linked shadows buffer
if (shadows.LastFrameUsed == currentFrame)
shadows.Reset();
shadows.LastFrameUsed = currentFrame;
shadows.MaxShadowsQuality = Math::Clamp(Math::Min<int32>((int32)Graphics::ShadowsQuality, (int32)renderContext.View.MaxShadowsQuality), 0, (int32)Quality::MAX - 1);
shadows.EnableStaticShadows = !renderContext.View.IsOfflinePass && !renderContext.View.IsSingleFrame;
shadows.EnableStaticShadows = !renderContext.View.IsOfflinePass && !renderContext.View.IsSingleFrame && !shadows.LinkedShadows;
int32 atlasResolution;
switch (Graphics::ShadowMapsQuality)
{
@@ -1325,6 +1357,29 @@ RETRY_ATLAS_SETUP:
SetupLight(shadows, renderContext, renderContextBatch, *(RenderSpotLightData*)light, atlasLight);
else //if (light->IsDirectionalLight)
SetupLight(shadows, renderContext, renderContextBatch, *(RenderDirectionalLightData*)light, atlasLight);
// Check if that light exists in linked shadows buffer to reuse shadow maps
const ShadowAtlasLight* linkedAtlasLight;
if (shadows.LinkedShadows && ((linkedAtlasLight = shadows.LinkedShadows->Lights.TryGet(light->ID))) && linkedAtlasLight->TilesCount == atlasLight.TilesCount)
{
for (int32 tileIndex = 0; tileIndex < atlasLight.TilesCount; tileIndex++)
{
auto& tile = atlasLight.Tiles[tileIndex];
tile.LinkedRectTile = nullptr;
auto& linkedTile = linkedAtlasLight->Tiles[tileIndex];
// Check if both lights use the same projections
if (tile.WorldToShadow == linkedTile.WorldToShadow && linkedTile.RectTile)
{
tile.LinkedRectTile = linkedTile.RectTile;
}
}
}
else
{
for (auto& tile : atlasLight.Tiles)
tile.LinkedRectTile = nullptr;
}
}
}
if (shadows.StaticAtlas.IsInitialized())
@@ -1495,29 +1550,21 @@ void ShadowsPass::RenderShadowMaps(RenderContextBatch& renderContextBatch)
// Set viewport for tile
context->SetViewportAndScissors(tile.CachedViewport);
if (tile.StaticRectTile && atlasLight.StaticState == ShadowAtlasLight::CopyStaticShadow)
if (tile.LinkedRectTile)
{
// Color.xyzw is used by PS_DepthCopy in Quad shader to scale input texture UVs
const float staticAtlasResolutionInv = 1.0f / shadows.StaticShadowMapAtlas->Width();
quadShaderData.Color = Float4(tile.StaticRectTile->Width, tile.StaticRectTile->Height, tile.StaticRectTile->X, tile.StaticRectTile->Y) * staticAtlasResolutionInv;
context->UpdateCB(quadShaderCB, &quadShaderData);
context->BindCB(0, quadShaderCB);
// Copy tile depth
context->BindSR(0, shadows.StaticShadowMapAtlas->View());
context->SetState(_psDepthCopy);
context->DrawFullscreenTriangle();
// Copy linked shadow
ASSERT(shadows.LinkedShadows);
CopyShadowMapTile(context, quadShaderCB, quadShaderData, shadows.LinkedShadows->ShadowMapAtlas, tile.LinkedRectTile);
}
else if (tile.StaticRectTile && atlasLight.StaticState == ShadowAtlasLight::CopyStaticShadow)
{
// Copy static shadow
CopyShadowMapTile(context, quadShaderCB, quadShaderData, shadows.StaticShadowMapAtlas, tile.StaticRectTile);
}
else if (!shadows.ClearShadowMapAtlas)
{
// Color.r is used by PS_DepthClear in Quad shader to clear depth
quadShaderData.Color = Float4::One;
context->UpdateCB(quadShaderCB, &quadShaderData);
context->BindCB(0, quadShaderCB);
// Clear tile depth
context->SetState(_psDepthClear);
context->DrawFullscreenTriangle();
// Clear shadow
ClearShadowMapTile(context, quadShaderCB, quadShaderData);
}
// Draw objects depth

View File

@@ -60,6 +60,8 @@ private:
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderDirectionalLightData& light, ShadowAtlasLight& atlasLight);
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderPointLightData& light, ShadowAtlasLight& atlasLight);
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderSpotLightData& light, ShadowAtlasLight& atlasLight);
void ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, struct QuadShaderData& quadShaderData) const;
void CopyShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, struct QuadShaderData& quadShaderData, const GPUTexture* srcShadowMap, const struct ShadowsAtlasRectTile* srcTile) const;
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)