Add ObjectRadius to DrawCall to properly cull local lights and env probes in Forward Shading

This commit is contained in:
Wojtek Figat
2023-09-20 15:01:02 +02:00
parent 8bd6649510
commit 8cd8ae93c3
12 changed files with 29 additions and 8 deletions

View File

@@ -76,11 +76,11 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
// Set reflection probe data
EnvironmentProbe* probe = nullptr;
// TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it
const Vector3 drawCallOrigin = drawCall.ObjectPosition + view.Origin;
const BoundingSphere objectBoundsWorld(drawCall.ObjectPosition + view.Origin, drawCall.ObjectRadius);
for (int32 i = 0; i < cache->EnvironmentProbes.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, Span<by
// Set local lights
data.LocalLightsCount = 0;
const BoundingSphere objectBounds(drawCall.ObjectPosition, drawCall.ObjectRadius);
// TODO: optimize lights searching for a transparent material - use spatial cache for renderer to find it
for (int32 i = 0; i < cache->PointLights.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, Span<by
for (int32 i = 0; i < cache->SpotLights.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++;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

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

View File

@@ -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++)

View File

@@ -258,6 +258,11 @@ struct DrawCall
/// </summary>
Float3 ObjectPosition;
/// <summary>
/// Object bounding sphere radius that contains it whole (sphere at ObjectPosition).
/// </summary>
float ObjectRadius;
/// <summary>
/// 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).
/// </summary>

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;