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) {