Refactor scene rendering to use batched culling for main view and shadow projections

This commit is contained in:
Wojtek Figat
2022-10-28 09:13:28 +02:00
parent e217d5e79b
commit eb52d333ae
44 changed files with 1064 additions and 514 deletions

View File

@@ -189,298 +189,37 @@ void ShadowsPass::updateShadowMapSize()
}
}
void ShadowsPass::Dispose()
void ShadowsPass::SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext)
{
// Base
RendererPass::Dispose();
// Cleanup
_psShadowDir.Delete();
_psShadowPoint.Delete();
_psShadowSpot.Delete();
_shader = nullptr;
_sphereModel = nullptr;
SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM);
SAFE_DELETE_GPU_RESOURCE(_shadowMapCube);
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPointLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light)
{
return _supportsShadows;
}
void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context)
{
ASSERT(IsReady());
auto& view = renderContext.View;
const auto shader = _shader->GetShader();
const auto shadowMapsQuality = Graphics::ShadowMapsQuality;
if (shadowMapsQuality != _currentShadowMapsQuality)
updateShadowMapSize();
auto shadowsQuality = Graphics::ShadowsQuality;
maxShadowsQuality = Math::Clamp(Math::Min<int32>(static_cast<int32>(shadowsQuality), static_cast<int32>(view.MaxShadowsQuality)), 0, static_cast<int32>(Quality::MAX) - 1);
const auto& view = renderContext.View;
// Use the current render view to sync model LODs with the shadow maps rendering stage
_shadowContext.LodProxyView = &renderContext.View;
shadowContext.LodProxyView = &renderContext.View;
// Prepare properties
auto& shadowView = _shadowContext.View;
auto& shadowView = shadowContext.View;
shadowView.Flags = view.Flags;
shadowView.StaticFlagsMask = view.StaticFlagsMask;
shadowView.RenderLayersMask = view.RenderLayersMask;
shadowView.IsOfflinePass = view.IsOfflinePass;
shadowView.ModelLODBias = view.ModelLODBias + view.ShadowModelLODBias;
shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor * view.ShadowModelLODDistanceFactor;
shadowView.ModelLODBias = view.ModelLODBias;
shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor;
shadowView.Pass = DrawPass::Depth;
shadowView.Origin = view.Origin;
_shadowContext.List = &_shadowCache;
shadowContext.List = RenderList::GetFromPool();
shadowContext.Buffers = renderContext.Buffers;
shadowContext.Task = renderContext.Task;
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererPointLightData& light, GPUTextureView* shadowMask)
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light)
{
const float sphereModelScale = 3.0f;
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
auto shader = _shader->GetShader();
Data sperLight;
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
Float3 lightDirection = light.Direction;
float dstLightToView = Float3::Distance(lightPosition, view.Position);
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
_shadowContext.View.SetUpCube(PointLight_NearPlane, lightRadius, lightPosition);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
// Set up view
_shadowCache.Clear();
_shadowContext.View.SetFace(faceIndex);
Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[faceIndex]);
// Set render target
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = 1;
sperLight.LightShadow.CascadeSplits = Float4::Zero;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(lightRadius * sphereModelScale, wvp);
Matrix::Translation(lightPosition, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererSpotLightData& light, GPUTextureView* shadowMask)
{
const float sphereModelScale = 3.0f;
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
auto shader = _shader->GetShader();
Data sperLight;
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
Float3 lightDirection = light.Direction;
float dstLightToView = Float3::Distance(lightPosition, view.Position);
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
_shadowContext.View.SetProjector(SpotLight_NearPlane, lightRadius, lightPosition, lightDirection, light.UpVector, light.OuterConeAngle * 2.0f);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
// Render depth to all 1 face of the cube map
const int32 cubeFaceIndex = 0;
{
// Set up view
_shadowCache.Clear();
Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[cubeFaceIndex]);
// Set render target
auto rt = _shadowMapCube->View(cubeFaceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = 1;
sperLight.LightShadow.CascadeSplits = Float4::Zero;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(lightRadius * sphereModelScale, wvp);
Matrix::Translation(lightPosition, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->View(cubeFaceIndex));
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(cubeFaceIndex), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask)
{
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
const RenderView& view = renderContext.View;
auto mainCache = renderContext.List;
auto shader = _shader->GetShader();
Data sperLight;
Float3 lightDirection = light.Direction;
float shadowsDistance = Math::Min(view.Far, light.ShadowsDistance);
int32 csmCount = Math::Clamp(light.CascadeCount, 0, MAX_CSM_CASCADES);
bool blendCSM = Graphics::AllowCSMBlending;
const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
#if USE_EDITOR
if (IsRunningRadiancePass)
blendCSM = false;
@@ -561,9 +300,7 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
// Convert distance splits to ratios cascade in the range [0, 1]
for (int32 i = 0; i < MAX_CSM_CASCADES; i++)
{
cascadeSplits[i] = (cascadeSplits[i] - cameraNear) / cameraRange;
}
}
// Select best Up vector
@@ -585,10 +322,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
Float3 frustumCorners[8];
Matrix shadowView, shadowProjection, shadowVP;
// Set up GPU context and render view
const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view);
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = csmCount;
shadowData.BlendCSM = blendCSM;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
// Create the different view and projection matrices for each split
float splitMinRatio = 0;
@@ -684,26 +424,213 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
0.5f, 0.5f, 0.0f, 1.0f);
Matrix m;
Matrix::Multiply(shadowVP, T, m);
Matrix::Transpose(m, sperLight.LightShadow.ShadowVP[cascadeIndex]);
Matrix::Transpose(m, shadowData.Constants.ShadowVP[cascadeIndex]);
}
// Set up view and cache
_shadowCache.Clear();
_shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position;
_shadowContext.View.Direction = lightDirection;
_shadowContext.View.SetUp(shadowView, shadowProjection);
_shadowContext.View.CullingFrustum.SetMatrix(cullingVP);
// Setup context for cascade
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position;
shadowContext.View.Direction = lightDirection;
shadowContext.View.SetUp(shadowView, shadowProjection);
shadowContext.View.CullingFrustum.SetMatrix(cullingVP);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view);
}
// Set render target
const auto rt = _shadowMapCSM->View(cascadeIndex);
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCSM;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = csmCount;
shadowData.Constants.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange;
}
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererPointLightData& light)
{
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = 6;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
const auto& view = renderContext.View;
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float dstLightToView = Float3::Distance(light.Position, view.Position);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.SetUpCube(PointLight_NearPlane, light.Radius, light.Position);
shadowContext.View.SetFace(faceIndex);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]);
}
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCube;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = 1;
shadowData.Constants.CascadeSplits = Float4::Zero;
}
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererSpotLightData& light)
{
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = 1;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
const auto& view = renderContext.View;
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float dstLightToView = Float3::Distance(light.Position, view.Position);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Render depth to all 1 face of the cube map
constexpr int32 faceIndex = 0;
{
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.SetProjector(SpotLight_NearPlane, light.Radius, light.Position, light.Direction, light.UpVector, light.OuterConeAngle * 2.0f);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]);
}
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCube;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = 1;
shadowData.Constants.CascadeSplits = Float4::Zero;
}
void ShadowsPass::Dispose()
{
// Base
RendererPass::Dispose();
// Cleanup
_psShadowDir.Delete();
_psShadowPoint.Delete();
_psShadowSpot.Delete();
_shader = nullptr;
_sphereModel = nullptr;
SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM);
SAFE_DELETE_GPU_RESOURCE(_shadowMapCube);
}
void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch)
{
PROFILE_CPU();
_shadowData.Clear();
auto& view = renderContext.View;
// Update shadow map
const auto shadowMapsQuality = Graphics::ShadowMapsQuality;
if (shadowMapsQuality != _currentShadowMapsQuality)
updateShadowMapSize();
auto shadowsQuality = Graphics::ShadowsQuality;
maxShadowsQuality = Math::Clamp(Math::Min<int32>(static_cast<int32>(shadowsQuality), static_cast<int32>(view.MaxShadowsQuality)), 0, static_cast<int32>(Quality::MAX) - 1);
// Create shadow projections for lights
for (auto& light : renderContext.List->DirectionalLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
for (auto& light : renderContext.List->PointLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
for (auto& light : renderContext.List->SpotLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererPointLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererSpotLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererDirectionalLightData& light)
{
return _supportsShadows;
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererPointLightData& light, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
auto shader = _shader->GetShader();
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
@@ -720,27 +647,168 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCSM;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = csmCount;
sperLight.LightShadow.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange;
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(light.Radius * sphereModelScale, wvp);
Matrix::Translation(light.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererSpotLightData& light, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
auto shader = _shader->GetShader();
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
// Render depth to all 1 face of the cube map
constexpr int32 faceIndex = 0;
{
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(light.Radius * sphereModelScale, wvp);
Matrix::Translation(light.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->View(faceIndex));
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(faceIndex), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM);
// Render shadow map for each projection
for (int32 cascadeIndex = 0; cascadeIndex < shadowData.ContextCount; cascadeIndex++)
{
const auto rt = _shadowMapCSM->View(cascadeIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(renderContext.Task->GetViewport());
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
auto& view = renderContext.View;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Render shadow in screen space
auto shader = _shader->GetShader();
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCSM->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast<int32>(Quality::MAX) * blendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0)));
context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast<int32>(Quality::MAX) * shadowData.BlendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0)));
context->DrawFullscreenTriangle();
// Cleanup