Fix artifacts due to light shape culling and use depth test to improve perf

This commit is contained in:
Wojtek Figat
2024-04-14 23:44:08 +02:00
parent 00f2a0b825
commit 25f3cef8c3
7 changed files with 113 additions and 114 deletions

View File

@@ -337,14 +337,14 @@ void RenderTools::ComputePitch(PixelFormat format, int32 width, int32 height, ui
case PixelFormat::ASTC_8x8_UNorm_sRGB:
case PixelFormat::ASTC_10x10_UNorm:
case PixelFormat::ASTC_10x10_UNorm_sRGB:
{
const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(format);
uint32 nbw = Math::Max<uint32>(1, Math::DivideAndRoundUp(width, blockSize));
uint32 nbh = Math::Max<uint32>(1, Math::DivideAndRoundUp(height, blockSize));
rowPitch = nbw * 16; // All ASTC blocks use 128 bits
slicePitch = rowPitch * nbh;
}
break;
{
const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(format);
uint32 nbw = Math::Max<uint32>(1, Math::DivideAndRoundUp(width, blockSize));
uint32 nbh = Math::Max<uint32>(1, Math::DivideAndRoundUp(height, blockSize));
rowPitch = nbw * 16; // All ASTC blocks use 128 bits
slicePitch = rowPitch * nbh;
}
break;
case PixelFormat::R8G8_B8G8_UNorm:
case PixelFormat::G8R8_G8B8_UNorm:
ASSERT(PixelFormatExtensions::IsPacked(format));
@@ -590,7 +590,7 @@ void RenderTools::CalculateTangentFrame(FloatR10G10B10A2& resultNormal, FloatR10
void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside)
{
// Construct world matrix
constexpr float sphereModelScale = 0.0202f; // Manually tweaked for 'Engine/Models/Sphere'
constexpr float sphereModelScale = 0.0205f; // Manually tweaked for 'Engine/Models/Sphere' with some slack
const float scaling = radius * sphereModelScale;
resultWorld = Matrix::Identity;
resultWorld.M11 = scaling;
@@ -601,10 +601,7 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo
resultWorld.M43 = position.Z;
// Check if view is inside the sphere
float viewToCenter = Float3::Distance(view.Position, position);
//if (radius + viewToCenter > view.Far)
// radius = view.Far - viewToCenter; // Clamp radius
resultIsViewInside = viewToCenter - radius < 5.0f; // Manually tweaked bias
resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias
}
int32 MipLevelsCount(int32 width, bool useMipLevels)

View File

@@ -31,20 +31,18 @@ bool LightPass::Init()
{
// Create pipeline states
_psLightDir.CreatePipelineStates();
_psLightPointNormal.CreatePipelineStates();
_psLightPointInverted.CreatePipelineStates();
_psLightSpotNormal.CreatePipelineStates();
_psLightSpotInverted.CreatePipelineStates();
_psLightSkyNormal = GPUDevice::Instance->CreatePipelineState();
_psLightSkyInverted = GPUDevice::Instance->CreatePipelineState();
_psLightPoint.CreatePipelineStates();
_psLightPointInside.CreatePipelineStates();
_psLightSpot.CreatePipelineStates();
_psLightSpotInside.CreatePipelineStates();
_psLightSky = GPUDevice::Instance->CreatePipelineState();
_psLightSkyInside = GPUDevice::Instance->CreatePipelineState();
// Load assets
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Lights"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Sphere"));
if (_shader == nullptr || _sphereModel == nullptr)
{
return true;
}
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind<LightPass, &LightPass::OnShaderReloading>(this);
@@ -90,46 +88,50 @@ bool LightPass::setupResources()
if (_psLightDir.Create(psDesc, shader, "PS_Directional"))
return true;
}
if (!_psLightPointNormal.IsValid() || !_psLightPointInverted.IsValid())
if (!_psLightPoint.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.CullMode = CullMode::TwoSided;
if (_psLightPointInverted.Create(psDesc, shader, "PS_Point"))
return true;
psDesc.CullMode = CullMode::Normal;
psDesc.DepthEnable = true;
if (_psLightPointNormal.Create(psDesc, shader, "PS_Point"))
psDesc.CullMode = CullMode::Normal;
if (_psLightPoint.Create(psDesc, shader, "PS_Point"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psLightPointInside.Create(psDesc, shader, "PS_Point"))
return true;
}
if (!_psLightSpotNormal.IsValid() || !_psLightSpotInverted.IsValid())
if (!_psLightSpot.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.CullMode = CullMode::TwoSided;
if (_psLightSpotInverted.Create(psDesc, shader, "PS_Spot"))
return true;
psDesc.CullMode = CullMode::Normal;
psDesc.DepthEnable = true;
if (_psLightSpotNormal.Create(psDesc, shader, "PS_Spot"))
psDesc.CullMode = CullMode::Normal;
if (_psLightSpot.Create(psDesc, shader, "PS_Spot"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psLightSpotInside.Create(psDesc, shader, "PS_Spot"))
return true;
}
if (!_psLightSkyNormal->IsValid() || !_psLightSkyInverted->IsValid())
if (!_psLightSky->IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.CullMode = CullMode::Normal;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.PS = shader->GetPS("PS_Sky");
if (_psLightSkyNormal->Init(psDesc))
psDesc.DepthEnable = true;
psDesc.CullMode = CullMode::Normal;
if (_psLightSky->Init(psDesc))
return true;
psDesc.CullMode = CullMode::TwoSided;
if (_psLightSkyInverted->Init(psDesc))
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psLightSkyInside->Init(psDesc))
return true;
}
@@ -143,12 +145,12 @@ void LightPass::Dispose()
// Cleanup
_psLightDir.Delete();
_psLightPointNormal.Delete();
_psLightPointInverted.Delete();
_psLightSpotNormal.Delete();
_psLightSpotInverted.Delete();
SAFE_DELETE_GPU_RESOURCE(_psLightSkyNormal);
SAFE_DELETE_GPU_RESOURCE(_psLightSkyInverted);
_psLightPoint.Delete();
_psLightPointInside.Delete();
_psLightSpot.Delete();
_psLightSpotInside.Delete();
SAFE_DELETE_GPU_RESOURCE(_psLightSky);
SAFE_DELETE_GPU_RESOURCE(_psLightSkyInside);
SAFE_DELETE_GPU_RESOURCE(_psClearDiffuse);
_sphereModel = nullptr;
}
@@ -298,7 +300,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(0, cb0);
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightPointInverted : _psLightPointNormal).Get(permutationIndex));
context->SetState((isViewInside ? _psLightPointInside : _psLightPoint).Get(permutationIndex));
sphereMesh.Render(context);
}
@@ -341,7 +343,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(0, cb0);
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightSpotInverted : _psLightSpotNormal).Get(permutationIndex));
context->SetState((isViewInside ? _psLightSpotInside : _psLightSpot).Get(permutationIndex));
sphereMesh.Render(context);
}
@@ -400,7 +402,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->UpdateCB(cb0, &perLight);
context->BindCB(0, cb0);
context->BindCB(1, cb1);
context->SetState(isViewInside ? _psLightSkyInverted : _psLightSkyNormal);
context->SetState(isViewInside ? _psLightSkyInside : _psLightSky);
sphereMesh.Render(context);
}

View File

@@ -16,12 +16,12 @@ class LightPass : public RendererPass<LightPass>
private:
AssetReference<Shader> _shader;
GPUPipelineStatePermutationsPs<2> _psLightDir;
GPUPipelineStatePermutationsPs<4> _psLightPointNormal;
GPUPipelineStatePermutationsPs<4> _psLightPointInverted;
GPUPipelineStatePermutationsPs<4> _psLightSpotNormal;
GPUPipelineStatePermutationsPs<4> _psLightSpotInverted;
GPUPipelineState* _psLightSkyNormal = nullptr;
GPUPipelineState* _psLightSkyInverted = nullptr;
GPUPipelineStatePermutationsPs<4> _psLightPoint;
GPUPipelineStatePermutationsPs<4> _psLightPointInside;
GPUPipelineStatePermutationsPs<4> _psLightSpot;
GPUPipelineStatePermutationsPs<4> _psLightSpotInside;
GPUPipelineState* _psLightSky = nullptr;
GPUPipelineState* _psLightSkyInside = nullptr;
GPUPipelineState* _psClearDiffuse = nullptr;
AssetReference<Model> _sphereModel;
PixelFormat _shadowMaskFormat;
@@ -44,12 +44,12 @@ private:
void OnShaderReloading(Asset* obj)
{
_psLightDir.Release();
_psLightPointNormal.Release();
_psLightPointInverted.Release();
_psLightSpotNormal.Release();
_psLightSpotInverted.Release();
_psLightSkyNormal->ReleaseGPU();
_psLightSkyInverted->ReleaseGPU();
_psLightPoint.Release();
_psLightPointInside.Release();
_psLightSpot.Release();
_psLightSpotInside.Release();
_psLightSky->ReleaseGPU();
_psLightSkyInside->ReleaseGPU();
invalidateResources();
}
#endif

View File

@@ -13,6 +13,12 @@
#include "Engine/Graphics/RenderTargetPool.h"
#include "Engine/Level/Actors/EnvironmentProbe.h"
PACK_STRUCT(struct Data {
ShaderEnvProbeData PData;
Matrix WVP;
ShaderGBufferData GBuffer;
});
#if GENERATE_GF_CACHE
// This code below (PreIntegratedGF namespace) is based on many Siggraph presentations about BRDF shading:
@@ -239,13 +245,6 @@ namespace PreIntegratedGF
class Model;
ReflectionsPass::ReflectionsPass()
: _psProbeNormal(nullptr)
, _psProbeInverted(nullptr)
, _psCombinePass(nullptr)
{
}
String ReflectionsPass::ToString() const
{
return TEXT("ReflectionsPass");
@@ -254,15 +253,13 @@ String ReflectionsPass::ToString() const
bool ReflectionsPass::Init()
{
#if GENERATE_GF_CACHE
// Generate cache
PreIntegratedGF::Generate();
#endif
// Create pipeline states
_psProbeNormal = GPUDevice::Instance->CreatePipelineState();
_psProbeInverted = GPUDevice::Instance->CreatePipelineState();
_psProbe = GPUDevice::Instance->CreatePipelineState();
_psProbeInside = GPUDevice::Instance->CreatePipelineState();
_psCombinePass = GPUDevice::Instance->CreatePipelineState();
// Load assets
@@ -294,17 +291,19 @@ bool ReflectionsPass::setupResources()
// Create pipeline stages
GPUPipelineState::Description psDesc;
if (!_psProbeNormal->IsValid() || !_psProbeInverted->IsValid())
if (!_psProbe->IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode = BlendingMode::AlphaBlend;
psDesc.CullMode = CullMode::Normal;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.PS = shader->GetPS("PS_EnvProbe");
if (_psProbeNormal->Init(psDesc))
psDesc.CullMode = CullMode::Normal;
psDesc.DepthEnable = true;
if (_psProbe->Init(psDesc))
return true;
psDesc.CullMode = CullMode::TwoSided;
if (_psProbeInverted->Init(psDesc))
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psProbeInside->Init(psDesc))
return true;
}
psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
@@ -326,8 +325,8 @@ void ReflectionsPass::Dispose()
RendererPass::Dispose();
// Cleanup
SAFE_DELETE_GPU_RESOURCE(_psProbeNormal);
SAFE_DELETE_GPU_RESOURCE(_psProbeInverted);
SAFE_DELETE_GPU_RESOURCE(_psProbe);
SAFE_DELETE_GPU_RESOURCE(_psProbeInside);
SAFE_DELETE_GPU_RESOURCE(_psCombinePass);
_shader = nullptr;
_sphereModel = nullptr;
@@ -416,7 +415,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
context->BindSR(4, probe.Texture);
context->SetState(isViewInside ? _psProbeInverted : _psProbeNormal);
context->SetState(isViewInside ? _psProbeInside : _psProbe);
sphereMesh.Render(context);
}

View File

@@ -16,30 +16,15 @@
class ReflectionsPass : public RendererPass<ReflectionsPass>
{
private:
PACK_STRUCT(struct Data {
ShaderEnvProbeData PData;
Matrix WVP;
ShaderGBufferData GBuffer;
});
AssetReference<Shader> _shader;
GPUPipelineState* _psProbeNormal;
GPUPipelineState* _psProbeInverted;
GPUPipelineState* _psCombinePass;
GPUPipelineState* _psProbe = nullptr;
GPUPipelineState* _psProbeInside = nullptr;
GPUPipelineState* _psCombinePass = nullptr;
AssetReference<Model> _sphereModel;
AssetReference<Texture> _preIntegratedGF;
public:
/// <summary>
/// Init
/// </summary>
ReflectionsPass();
public:
/// <summary>
/// Perform reflections pass rendering for the input task.
/// </summary>
@@ -48,7 +33,6 @@ public:
void Render(RenderContext& renderContext, GPUTextureView* lightBuffer);
public:
// [RendererPass]
String ToString() const override;
bool Init() override;
@@ -56,15 +40,14 @@ public:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)
{
_psProbeNormal->ReleaseGPU();
_psProbeInverted->ReleaseGPU();
_psProbe->ReleaseGPU();
_psProbeInside->ReleaseGPU();
_psCombinePass->ReleaseGPU();
invalidateResources();
}
#endif
protected:
// [RendererPass]
bool setupResources() override;
};

View File

@@ -446,7 +446,9 @@ bool ShadowsPass::Init()
// Create pipeline states
_psShadowDir.CreatePipelineStates();
_psShadowPoint.CreatePipelineStates();
_psShadowPointInside.CreatePipelineStates();
_psShadowSpot.CreatePipelineStates();
_psShadowSpotInside.CreatePipelineStates();
// Load assets
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Shadows"));
@@ -496,27 +498,40 @@ bool ShadowsPass::setupResources()
// Create pipeline stages
GPUPipelineState::Description psDesc;
if (!_psShadowPoint.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.CullMode = CullMode::TwoSided;
psDesc.VS = shader->GetVS("VS_Model");
if (_psShadowPoint.Create(psDesc, shader, "PS_PointLight"))
return true;
}
if (!_psShadowDir.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RG;
if (_psShadowDir.Create(psDesc, shader, "PS_DirLight"))
return true;
}
if (!_psShadowPoint.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RG;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.DepthEnable = true;
psDesc.CullMode = CullMode::Normal;
if (_psShadowPoint.Create(psDesc, shader, "PS_PointLight"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psShadowPointInside.Create(psDesc, shader, "PS_PointLight"))
return true;
}
if (!_psShadowSpot.IsValid())
{
psDesc = GPUPipelineState::Description::DefaultNoDepth;
psDesc.CullMode = CullMode::TwoSided;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RG;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.DepthEnable = true;
psDesc.CullMode = CullMode::Normal;
if (_psShadowSpot.Create(psDesc, shader, "PS_SpotLight"))
return true;
psDesc.DepthFunc = ComparisonFunc::Greater;
psDesc.CullMode = CullMode::Inverted;
if (_psShadowSpotInside.Create(psDesc, shader, "PS_SpotLight"))
return true;
}
if (_psDepthClear == nullptr)
{
@@ -994,7 +1009,9 @@ void ShadowsPass::Dispose()
// Cleanup
_psShadowDir.Delete();
_psShadowPoint.Delete();
_psShadowPointInside.Delete();
_psShadowSpot.Delete();
_psShadowSpotInside.Delete();
_shader = nullptr;
_sphereModel = nullptr;
SAFE_DELETE_GPU_RESOURCE(_psDepthClear);
@@ -1072,7 +1089,6 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
}
if (shadows.StaticAtlasTiles && (float)shadows.StaticAtlasPixelsUsed / (shadows.StaticAtlasTiles->Width * shadows.StaticAtlasTiles->Height) < SHADOWS_MAX_STATIC_ATLAS_CAPACITY_TO_DEFRAG)
{
float app = (float)shadows.StaticAtlasPixelsUsed / (shadows.StaticAtlasTiles->Width * shadows.StaticAtlasTiles->Height);
// Defragment static shadow atlas if it failed to insert any light but it's still should have space
bool anyStaticFailed = false;
for (auto& e : shadows.Lights)
@@ -1478,12 +1494,12 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
sperLight.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = EnumHasAnyFlags(view.Flags, ViewFlags::ContactShadows) ? light.ContactShadowsLength : 0.0f;
bool isViewInside;
if (isLocalLight)
{
// Calculate world view projection matrix for the light sphere
Matrix world, wvp;
bool isInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, ((RenderLocalLightData&)light).Radius, world, isInside);
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, ((RenderLocalLightData&)light).Radius, world, isViewInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
}
@@ -1498,12 +1514,12 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
context->SetRenderTarget(shadowMask);
if (light.IsPointLight)
{
context->SetState(_psShadowPoint.Get(permutationIndex));
context->SetState((isViewInside ? _psShadowPointInside : _psShadowPoint).Get(permutationIndex));
_sphereModel->LODs.Get()[0].Meshes.Get()[0].Render(context);
}
else if (light.IsSpotLight)
{
context->SetState(_psShadowSpot.Get(permutationIndex));
context->SetState((isViewInside ? _psShadowSpotInside : _psShadowSpot).Get(permutationIndex));
_sphereModel->LODs.Get()[0].Meshes.Get()[0].Render(context);
}
else //if (light.IsDirectionalLight)

View File

@@ -21,7 +21,9 @@ private:
GPUPipelineState* _psDepthCopy = nullptr;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowDir;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowPoint;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowPointInside;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowSpot;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowSpotInside;
PixelFormat _shadowMapFormat; // Cached on initialization
public: