From 8cd8ae93c3281e525970931eb380fc05e6ea8afb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Sep 2023 15:01:02 +0200 Subject: [PATCH] Add `ObjectRadius` to `DrawCall` to properly cull local lights and env probes in Forward Shading --- .../Graphics/Materials/MaterialShaderFeatures.cpp | 10 ++++++---- Source/Engine/Graphics/Models/Mesh.cpp | 3 +++ Source/Engine/Graphics/Models/SkinnedMesh.cpp | 2 ++ Source/Engine/Level/Actors/Skybox.cpp | 1 + Source/Engine/Level/Actors/SplineModel.cpp | 1 + Source/Engine/Particles/Particles.cpp | 3 ++- Source/Engine/Renderer/DrawCall.h | 5 +++++ Source/Engine/Renderer/GBufferPass.cpp | 1 + Source/Engine/Renderer/RenderList.cpp | 1 + Source/Engine/Terrain/TerrainChunk.cpp | 6 ++++-- Source/Engine/Terrain/TerrainChunk.h | 3 ++- Source/Engine/UI/TextRender.cpp | 1 + 12 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index b3069b4db..d8d69dc3e 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -76,11 +76,11 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanEnvironmentProbes.Count(); i++) { const auto p = cache->EnvironmentProbes[i]; - if (p->GetSphere().Contains(drawCallOrigin) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBoundsWorld, p->GetSphere())) { probe = p; break; @@ -99,10 +99,12 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanPointLights.Count() && data.LocalLightsCount < MaxLocalLights; i++) { const auto& light = cache->PointLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.ObjectPosition) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBounds, BoundingSphere(light.Position, light.Radius))) { light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false); data.LocalLightsCount++; @@ -111,7 +113,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanSpotLights.Count() && data.LocalLightsCount < MaxLocalLights; i++) { const auto& light = cache->SpotLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.ObjectPosition) != ContainmentType::Disjoint) + if (CollisionsHelper::SphereIntersectsSphere(objectBounds, BoundingSphere(light.Position, light.Radius))) { light.SetupLightData(&data.LocalLights[data.LocalLightsCount], false); data.LocalLightsCount++; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index e5ba5488d..558b53b18 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -429,6 +429,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons drawCall.Material = material; drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue(); drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = world; drawCall.Surface.Lightmap = nullptr; @@ -495,6 +496,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; @@ -555,6 +557,7 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index b6c966508..f6d24c335 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -198,6 +198,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = nullptr; @@ -258,6 +259,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition? drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; drawCall.Surface.Lightmap = nullptr; diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp index 7d35a2776..455c229c0 100644 --- a/Source/Engine/Level/Actors/Skybox.cpp +++ b/Source/Engine/Level/Actors/Skybox.cpp @@ -100,6 +100,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M Platform::MemoryClear(&drawCall, sizeof(DrawCall)); drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = GetPerInstanceRandom(); diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 6ca841b41..6b7916e4d 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -405,6 +405,7 @@ void SplineModel::Draw(RenderContext& renderContext) const Transform splineTransform = GetTransform(); renderContext.View.GetWorldMatrix(splineTransform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); for (int32 segment = 0; segment < _instances.Count(); segment++) { diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 873c4bd52..52234fd7a 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -938,7 +938,8 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe // Setup a draw call common data DrawCall drawCall; drawCall.PerInstanceRandom = effect->GetPerInstanceRandom(); - drawCall.ObjectPosition = effect->GetPosition(); + drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin; + drawCall.ObjectRadius = effect->GetSphere().Radius; // Draw all emitters for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++) diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 9fd6c13b5..d51102166 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -258,6 +258,11 @@ struct DrawCall /// Float3 ObjectPosition; + /// + /// Object bounding sphere radius that contains it whole (sphere at ObjectPosition). + /// + float ObjectRadius; + /// /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling). /// diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index 0e5e8508a..f824d2161 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -464,6 +464,7 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light transform.Scale *= decal->GetSize(); renderContext.View.GetWorldMatrix(transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = decal->GetSphere().Radius; context->ResetRenderTarget(); diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 1a4cc0205..f24f25128 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -855,6 +855,7 @@ DRAW: { auto& batch = BatchedDrawCalls.Get()[list.PreBatchedDrawCalls.Get()[i]]; auto drawCall = batch.DrawCall; + drawCall.ObjectRadius = 0.0f; bindParams.FirstDrawCall = &drawCall; const auto* instancesData = batch.Instances.Get(); diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 7db786ec2..737ce08f8 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -45,7 +45,7 @@ bool TerrainChunk::PrepareDraw(const RenderContext& renderContext) // Calculate chunk distance to view const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); - const float distance = Float3::Distance(_boundsCenter - lodView->Origin, lodView->Position); + const float distance = Float3::Distance(_sphere.Center - lodView->Origin, lodView->Position); lod = (int32)Math::Pow(distance / chunkEdgeSize, lodDistribution); lod += lodBias; @@ -88,6 +88,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const drawCall.Material = _cachedDrawMaterial; renderContext.View.GetWorldMatrix(_transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Terrain.Patch = _patch; drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); @@ -145,6 +146,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi drawCall.Material = material; renderContext.View.GetWorldMatrix(_transform, drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Terrain.Patch = _patch; drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); @@ -202,7 +204,7 @@ void TerrainChunk::UpdateBounds() OrientedBoundingBox obb(Vector3::Zero, Vector3::One); obb.Transform(localTransform); obb.GetBoundingBox(_bounds); - _boundsCenter = _bounds.GetCenter(); + BoundingSphere::FromBox(_bounds, _sphere); _bounds.Minimum -= boundsExtent; _bounds.Maximum += boundsExtent; diff --git a/Source/Engine/Terrain/TerrainChunk.h b/Source/Engine/Terrain/TerrainChunk.h index dd74a18a1..884160b45 100644 --- a/Source/Engine/Terrain/TerrainChunk.h +++ b/Source/Engine/Terrain/TerrainChunk.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Math/BoundingBox.h" +#include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Math/Transform.h" #include "Engine/Core/ISerializable.h" @@ -29,7 +30,7 @@ private: Float4 _heightmapUVScaleBias; Transform _transform; BoundingBox _bounds; - Vector3 _boundsCenter; + BoundingSphere _sphere; float _perInstanceRandom; float _yOffset, _yHeight; diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index cd60abddf..d161db0ee 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -366,6 +366,7 @@ void TextRender::Draw(RenderContext& renderContext) DrawCall drawCall; drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.ObjectRadius = _sphere.Radius; drawCall.Surface.GeometrySize = _localBox.GetSize(); drawCall.Surface.PrevWorld = _drawState.PrevWorld; drawCall.Surface.Lightmap = nullptr;