Graphics/ForwardFramePipeline: Frustum cull lights

This commit is contained in:
Jérôme Leclercq 2022-02-02 19:39:46 +01:00
parent 29cd77db55
commit de7fee348a
10 changed files with 33 additions and 18 deletions

View File

@ -187,7 +187,7 @@ int main()
entt::entity headingEntity = registry.create(); entt::entity headingEntity = registry.create();
{ {
auto& entityLight = registry.emplace<Nz::LightComponent>(playerEntity); auto& entityLight = registry.emplace<Nz::LightComponent>(playerEntity);
entityLight.AttachLight(std::make_shared<Nz::DirectionalLight>(), 1); entityLight.AttachLight(std::make_shared<Nz::SpotLight>(), 1);
auto& entityGfx = registry.emplace<Nz::GraphicsComponent>(playerEntity); auto& entityGfx = registry.emplace<Nz::GraphicsComponent>(playerEntity);
entityGfx.AttachRenderable(model, 1); entityGfx.AttachRenderable(model, 1);

View File

@ -26,7 +26,7 @@ namespace Nz
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
void FillLightData(void* data) override; void FillLightData(void* data) const override;
inline float GetAmbientFactor() const; inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const; inline float GetDiffuseFactor() const;

View File

@ -156,7 +156,8 @@ namespace Nz
std::unordered_set<WorldInstancePtr> m_removedWorldInstances; std::unordered_set<WorldInstancePtr> m_removedWorldInstances;
std::vector<std::unique_ptr<ElementRenderer>> m_elementRenderers; std::vector<std::unique_ptr<ElementRenderer>> m_elementRenderers;
std::vector<ElementRenderer::RenderStates> m_renderStates; std::vector<ElementRenderer::RenderStates> m_renderStates;
std::vector<Light*> m_visibleLights; std::vector<const Light*> m_renderableLights;
std::vector<const Light*> m_visibleLights;
std::vector<LightDataUbo> m_lightDataBuffers; std::vector<LightDataUbo> m_lightDataBuffers;
std::vector<VisibleRenderable> m_visibleRenderables; std::vector<VisibleRenderable> m_visibleRenderables;
BakedFrameGraph m_bakedFrameGraph; BakedFrameGraph m_bakedFrameGraph;

View File

@ -31,7 +31,7 @@ namespace Nz
virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0; virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0;
virtual void FillLightData(void* data) = 0; virtual void FillLightData(void* data) const = 0;
inline const BoundingVolumef& GetBoundingVolume() const; inline const BoundingVolumef& GetBoundingVolume() const;

View File

@ -25,7 +25,7 @@ namespace Nz
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
void FillLightData(void* data) override; void FillLightData(void* data) const override;
inline float GetAmbientFactor() const; inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const; inline float GetDiffuseFactor() const;

View File

@ -26,7 +26,7 @@ namespace Nz
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override;
void FillLightData(void* data) override; void FillLightData(void* data) const override;
inline float GetAmbientFactor() const; inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const; inline float GetDiffuseFactor() const;

View File

@ -18,7 +18,7 @@ namespace Nz
return -std::numeric_limits<float>::infinity(); return -std::numeric_limits<float>::infinity();
} }
void DirectionalLight::FillLightData(void* data) void DirectionalLight::FillLightData(void* data) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); auto lightOffset = PredefinedLightData::GetOffsets();

View File

@ -244,7 +244,7 @@ namespace Nz
BoundingVolumef boundingVolume(renderable->GetAABB()); BoundingVolumef boundingVolume(renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix()); boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume.aabb)) if (!frustum.Contains(boundingVolume))
continue; continue;
auto& visibleRenderable = m_visibleRenderables.emplace_back(); auto& visibleRenderable = m_visibleRenderables.emplace_back();
@ -259,6 +259,20 @@ namespace Nz
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(worldInstance.get())); visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(worldInstance.get()));
} }
// TODO: Lights update shouldn't trigger a rebuild of the depth prepass
m_visibleLights.clear();
for (auto&& [light, lightData] : m_lights)
{
const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
// TODO: Use more precise tests for point lights (frustum/sphere is cheap)
if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume))
{
m_visibleLights.push_back(light);
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(light));
}
}
if (viewerData.visibilityHash != visibilityHash) if (viewerData.visibilityHash != visibilityHash)
{ {
viewerData.rebuildDepthPrepass = true; viewerData.rebuildDepthPrepass = true;
@ -317,26 +331,26 @@ namespace Nz
renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix()); renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix());
// Select lights (TODO: Cull lights in frustum) // Select lights (TODO: Cull lights in frustum)
m_visibleLights.clear(); m_renderableLights.clear();
for (auto&& [light, lightData] : m_lights) for (const Light* light : m_visibleLights)
{ {
const BoundingVolumef& boundingVolume = light->GetBoundingVolume(); const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
if ((renderMask & lightData.renderMask) && boundingVolume.Intersect(renderableBoundingVolume.aabb)) if (boundingVolume.Intersect(renderableBoundingVolume.aabb))
m_visibleLights.push_back(light); m_renderableLights.push_back(light);
} }
// Sort lights // Sort lights
std::sort(m_visibleLights.begin(), m_visibleLights.end(), [&](Light* lhs, Light* rhs) std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const Light* lhs, const Light* rhs)
{ {
return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume); return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume);
}); });
std::size_t lightCount = std::min(m_visibleLights.size(), MaxLightCountPerDraw); std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw);
LightKey lightKey; LightKey lightKey;
lightKey.fill(nullptr); lightKey.fill(nullptr);
for (std::size_t i = 0; i < lightCount; ++i) for (std::size_t i = 0; i < lightCount; ++i)
lightKey[i] = m_visibleLights[i]; lightKey[i] = m_renderableLights[i];
RenderBufferView lightUboView; RenderBufferView lightUboView;
@ -383,7 +397,7 @@ namespace Nz
UInt8* lightPtr = static_cast<UInt8*>(lightDataPtr) + lightOffsets.lightsOffset; UInt8* lightPtr = static_cast<UInt8*>(lightDataPtr) + lightOffsets.lightsOffset;
for (std::size_t i = 0; i < lightCount; ++i) for (std::size_t i = 0; i < lightCount; ++i)
{ {
m_visibleLights[i]->FillLightData(lightPtr); m_renderableLights[i]->FillLightData(lightPtr);
lightPtr += lightOffsets.lightSize; lightPtr += lightOffsets.lightSize;
} }

View File

@ -18,7 +18,7 @@ namespace Nz
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter()); return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter());
} }
void PointLight::FillLightData(void* data) void PointLight::FillLightData(void* data) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); auto lightOffset = PredefinedLightData::GetOffsets();

View File

@ -18,7 +18,7 @@ namespace Nz
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter()); return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter());
} }
void SpotLight::FillLightData(void* data) void SpotLight::FillLightData(void* data) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); auto lightOffset = PredefinedLightData::GetOffsets();