Optimize local lights sphere mesh rendering to match the area better

This commit is contained in:
Wojtek Figat
2024-04-05 12:48:09 +02:00
parent 0cc6669cbd
commit 3efd1e4e84
6 changed files with 53 additions and 91 deletions

View File

@@ -890,7 +890,7 @@ public:
/// <param name="translation">The translation.</param>
/// <param name="rotation">Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.</param>
/// <param name="scaling">The scaling.</param>
/// <param name="result">When the method completes, contains the created rotation matrix.</param>
/// <param name="result">When the method completes, contains the created transformation matrix.</param>
static void Transformation(const Float3& scaling, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 3D affine transformation matrix.

View File

@@ -587,6 +587,26 @@ void RenderTools::CalculateTangentFrame(FloatR10G10B10A2& resultNormal, FloatR10
resultTangent = Float1010102(tangent * 0.5f + 0.5f, sign);
}
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'
const float scaling = radius * sphereModelScale;
resultWorld = Matrix::Identity;
resultWorld.M11 = scaling;
resultWorld.M22 = scaling;
resultWorld.M33 = scaling;
resultWorld.M41 = position.X;
resultWorld.M42 = position.Y;
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
}
int32 MipLevelsCount(int32 width, bool useMipLevels)
{
if (!useMipLevels)
@@ -644,22 +664,6 @@ int32 MipLevelsCount(int32 width, int32 height, int32 depth, bool useMipLevels)
return result;
}
float ViewToCenterLessRadius(const RenderView& view, const Float3& center, float radius)
{
// Calculate distance from view to sphere center
float viewToCenter = Float3::Distance(view.Position, center);
// Check if need to fix the radius
//if (radius + viewToCenter > view.Far)
{
// Clamp radius
//radius = view.Far - viewToCenter;
}
// Calculate result
return viewToCenter - radius;
}
void MeshBase::SetMaterialSlotIndex(int32 value)
{
if (value < 0 || value >= _model->MaterialSlots.Count())

View File

@@ -127,6 +127,8 @@ public:
static void CalculateTangentFrame(FloatR10G10B10A2& resultNormal, FloatR10G10B10A2& resultTangent, const Float3& normal);
static void CalculateTangentFrame(FloatR10G10B10A2& resultNormal, FloatR10G10B10A2& resultTangent, const Float3& normal, const Float3& tangent);
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
};
// Calculate mip levels count for a texture 1D
@@ -149,12 +151,3 @@ extern int32 MipLevelsCount(int32 width, int32 height, bool useMipLevels = true)
// @param useMipLevels True if use mip levels, otherwise false (use only 1 mip)
// @returns Mip levels count
extern int32 MipLevelsCount(int32 width, int32 height, int32 depth, bool useMipLevels = true);
/// <summary>
/// Calculate distance from view center to the sphere center less sphere radius, clamped to fit view far plane
/// </summary>
/// <param name="view">Render View</param>
/// <param name="center">Sphere center</param>
/// <param name="radius">Sphere radius</param>
/// <returns>Distance from view center to the sphere center less sphere radius</returns>
extern float ViewToCenterLessRadius(const RenderView& view, const Float3& center, float radius);

View File

@@ -40,7 +40,7 @@ bool LightPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Lights"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/SphereLowPoly"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Sphere"));
if (_shader == nullptr || _sphereModel == nullptr)
{
return true;
@@ -96,7 +96,7 @@ bool LightPass::setupResources()
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.CullMode = CullMode::Inverted;
psDesc.CullMode = CullMode::TwoSided;
if (_psLightPointInverted.Create(psDesc, shader, "PS_Point"))
return true;
psDesc.CullMode = CullMode::Normal;
@@ -110,7 +110,7 @@ bool LightPass::setupResources()
psDesc.BlendMode = BlendingMode::Add;
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
psDesc.VS = shader->GetVS("VS_Model");
psDesc.CullMode = CullMode::Inverted;
psDesc.CullMode = CullMode::TwoSided;
if (_psLightSpotInverted.Create(psDesc, shader, "PS_Spot"))
return true;
psDesc.CullMode = CullMode::Normal;
@@ -128,7 +128,7 @@ bool LightPass::setupResources()
psDesc.PS = shader->GetPS("PS_Sky");
if (_psLightSkyNormal->Init(psDesc))
return true;
psDesc.CullMode = CullMode::Inverted;
psDesc.CullMode = CullMode::TwoSided;
if (_psLightSkyInverted->Init(psDesc))
return true;
}
@@ -183,7 +183,6 @@ void LightPass::SetupLights(RenderContext& renderContext, RenderContextBatch& re
void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer)
{
const float sphereModelScale = 3.0f;
if (checkIfSkipPass())
return;
PROFILE_GPU_CPU("Lights");
@@ -230,6 +229,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
// Temporary data
PerLight perLight;
PerFrame perFrame;
auto& sphereMesh = _sphereModel->LODs.Get()[0].Meshes.Get()[0];
// Bind output
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
@@ -266,19 +266,12 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
{
PROFILE_GPU_CPU_NAMED("Point Light");
auto& light = mainCache->PointLights[lightIndex];
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
bool useIES = light.IESTexture != nullptr;
// Get distance from view center to light center less radius (check if view is inside a sphere)
float distance = ViewToCenterLessRadius(view, lightPosition, lightRadius * sphereModelScale);
bool isViewInside = distance < 0;
// 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 world, wvp;
bool isViewInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, light.Radius, world, isViewInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
// Fullscreen shadow mask rendering
@@ -306,7 +299,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightPointInverted : _psLightPointNormal).Get(permutationIndex));
_sphereModel->Render(context);
sphereMesh.Render(context);
}
context->UnBindCB(0);
@@ -316,19 +309,12 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
{
PROFILE_GPU_CPU_NAMED("Spot Light");
auto& light = mainCache->SpotLights[lightIndex];
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
bool useIES = light.IESTexture != nullptr;
// Get distance from view center to light center less radius (check if view is inside a sphere)
float distance = ViewToCenterLessRadius(view, lightPosition, lightRadius * sphereModelScale);
bool isViewInside = distance < 0;
// 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 world, wvp;
bool isViewInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, light.Radius, world, isViewInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
// Fullscreen shadow mask rendering
@@ -356,7 +342,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(1, cb1);
int32 permutationIndex = (disableSpecular ? 1 : 0) + (useIES ? 2 : 0);
context->SetState((isViewInside ? _psLightSpotInverted : _psLightSpotNormal).Get(permutationIndex));
_sphereModel->Render(context);
sphereMesh.Render(context);
}
context->UnBindCB(0);
@@ -395,21 +381,12 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
for (int32 lightIndex = 0; lightIndex < mainCache->SkyLights.Count(); lightIndex++)
{
PROFILE_GPU_CPU_NAMED("Sky Light");
// Cache data
auto& light = mainCache->SkyLights[lightIndex];
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
// Get distance from view center to light center less radius (check if view is inside a sphere)
float distance = ViewToCenterLessRadius(view, lightPosition, lightRadius * sphereModelScale);
bool isViewInside = distance < 0;
// 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 world, wvp;
bool isViewInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, light.Radius, world, isViewInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
// Pack light properties buffer
@@ -424,7 +401,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
context->BindCB(0, cb0);
context->BindCB(1, cb1);
context->SetState(isViewInside ? _psLightSkyInverted : _psLightSkyNormal);
_sphereModel->Render(context);
sphereMesh.Render(context);
}
RenderTargetPool::Release(shadowMask);

View File

@@ -267,12 +267,10 @@ bool ReflectionsPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Reflections"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/SphereLowPoly"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Sphere"));
_preIntegratedGF = Content::LoadAsyncInternal<Texture>(PRE_INTEGRATED_GF_ASSET_NAME);
if (_shader == nullptr || _sphereModel == nullptr || _preIntegratedGF == nullptr)
{
return true;
}
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind<ReflectionsPass, &ReflectionsPass::OnShaderReloading>(this);
#endif
@@ -305,7 +303,7 @@ bool ReflectionsPass::setupResources()
psDesc.PS = shader->GetPS("PS_EnvProbe");
if (_psProbeNormal->Init(psDesc))
return true;
psDesc.CullMode = CullMode::Inverted;
psDesc.CullMode = CullMode::TwoSided;
if (_psProbeInverted->Init(psDesc))
return true;
}
@@ -399,21 +397,15 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
Sorting::QuickSort(renderContext.List->EnvironmentProbes.Get(), renderContext.List->EnvironmentProbes.Count(), &SortProbes);
// Render all env probes
auto& sphereMesh = _sphereModel->LODs.Get()[0].Meshes.Get()[0];
for (int32 i = 0; i < probesCount; i++)
{
// Cache data
const RenderEnvironmentProbeData& probe = renderContext.List->EnvironmentProbes.Get()[i];
// Get distance from view center to light center less radius (check if view is inside a sphere)
const float sphereModelScale = 2.0f;
float distance = ViewToCenterLessRadius(view, probe.Position, probe.Radius);
bool isViewInside = distance < 0;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(probe.Radius * sphereModelScale, wvp);
Matrix::Translation(probe.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix world, wvp;
bool isViewInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, probe.Position, probe.Radius, world, isViewInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
// Pack probe properties buffer
@@ -424,9 +416,8 @@ 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);
_sphereModel->Render(context);
sphereMesh.Render(context);
}
context->UnBindSR(4);

View File

@@ -158,7 +158,7 @@ bool ShadowsPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Shadows"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/SphereLowPoly"));
_sphereModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Sphere"));
if (_shader == nullptr || _sphereModel == nullptr)
return true;
@@ -891,7 +891,6 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
RenderContext& renderContext = renderContextBatch.GetMainContext();
const ShadowsCustomBuffer& shadows = *renderContext.Buffers->FindCustomBuffer<ShadowsCustomBuffer>(TEXT("Shadows"));
ASSERT(shadows.LastFrameUsed == Engine::FrameCount);
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
auto shader = _shader->GetShader();
const bool isLocalLight = light.IsPointLight || light.IsSpotLight;
@@ -922,14 +921,12 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
if (isLocalLight)
{
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(((RenderLocalLightData&)light).Radius * sphereModelScale, wvp);
Matrix::Translation(light.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix world, wvp;
bool isInside;
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, light.Position, ((RenderLocalLightData&)light).Radius, world, isInside);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
}
// TODO: reimplement cascades blending for directional lights (but with dithering)
// Render shadow in screen space
GPUConstantBuffer* cb0 = shader->GetCB(0);
@@ -942,12 +939,12 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
if (light.IsPointLight)
{
context->SetState(_psShadowPoint.Get(permutationIndex));
_sphereModel->Render(context);
_sphereModel->LODs.Get()[0].Meshes.Get()[0].Render(context);
}
else if (light.IsSpotLight)
{
context->SetState(_psShadowSpot.Get(permutationIndex));
_sphereModel->Render(context);
_sphereModel->LODs.Get()[0].Meshes.Get()[0].Render(context);
}
else //if (light.IsDirectionalLight)
{