diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h
index 0a76a98d1..03a34ecc8 100644
--- a/Source/Engine/Core/Math/Matrix.h
+++ b/Source/Engine/Core/Math/Matrix.h
@@ -890,7 +890,7 @@ public:
/// The translation.
/// Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
/// The scaling.
- /// When the method completes, contains the created rotation matrix.
+ /// When the method completes, contains the created transformation matrix.
static void Transformation(const Float3& scaling, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 3D affine transformation matrix.
diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp
index edd30af7a..1679326bc 100644
--- a/Source/Engine/Graphics/RenderTools.cpp
+++ b/Source/Engine/Graphics/RenderTools.cpp
@@ -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())
diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h
index 07fcab89a..00d3398cd 100644
--- a/Source/Engine/Graphics/RenderTools.h
+++ b/Source/Engine/Graphics/RenderTools.h
@@ -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);
-
-///
-/// Calculate distance from view center to the sphere center less sphere radius, clamped to fit view far plane
-///
-/// Render View
-/// Sphere center
-/// Sphere radius
-/// Distance from view center to the sphere center less sphere radius
-extern float ViewToCenterLessRadius(const RenderView& view, const Float3& center, float radius);
diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp
index e1330ece5..72fcc5a0c 100644
--- a/Source/Engine/Renderer/LightPass.cpp
+++ b/Source/Engine/Renderer/LightPass.cpp
@@ -40,7 +40,7 @@ bool LightPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal(TEXT("Shaders/Lights"));
- _sphereModel = Content::LoadAsyncInternal(TEXT("Engine/Models/SphereLowPoly"));
+ _sphereModel = Content::LoadAsyncInternal(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);
diff --git a/Source/Engine/Renderer/ReflectionsPass.cpp b/Source/Engine/Renderer/ReflectionsPass.cpp
index 9a136d354..3a548f8ab 100644
--- a/Source/Engine/Renderer/ReflectionsPass.cpp
+++ b/Source/Engine/Renderer/ReflectionsPass.cpp
@@ -267,12 +267,10 @@ bool ReflectionsPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal(TEXT("Shaders/Reflections"));
- _sphereModel = Content::LoadAsyncInternal(TEXT("Engine/Models/SphereLowPoly"));
+ _sphereModel = Content::LoadAsyncInternal(TEXT("Engine/Models/Sphere"));
_preIntegratedGF = Content::LoadAsyncInternal(PRE_INTEGRATED_GF_ASSET_NAME);
if (_shader == nullptr || _sphereModel == nullptr || _preIntegratedGF == nullptr)
- {
return true;
- }
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind(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);
diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp
index aa304d43b..7da06041c 100644
--- a/Source/Engine/Renderer/ShadowsPass.cpp
+++ b/Source/Engine/Renderer/ShadowsPass.cpp
@@ -158,7 +158,7 @@ bool ShadowsPass::Init()
// Load assets
_shader = Content::LoadAsyncInternal(TEXT("Shaders/Shadows"));
- _sphereModel = Content::LoadAsyncInternal(TEXT("Engine/Models/SphereLowPoly"));
+ _sphereModel = Content::LoadAsyncInternal(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(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)
{