Graphics: Move shadow-mapping related code to LightShadow classes

This commit is contained in:
SirLynix 2022-11-26 18:33:12 +01:00 committed by Jérôme Leclercq
parent ec3bc45544
commit 1768f20365
27 changed files with 496 additions and 160 deletions

View File

@ -25,7 +25,7 @@ namespace Nz
virtual const Color& GetClearColor() const = 0;
virtual UInt32 GetRenderMask() const = 0;
virtual const RenderTarget& GetRenderTarget() = 0;
virtual const RenderTarget& GetRenderTarget() const = 0;
virtual ViewerInstance& GetViewerInstance() = 0;
virtual const ViewerInstance& GetViewerInstance() const = 0;
virtual const Recti& GetViewport() const = 0;

View File

@ -32,7 +32,7 @@ namespace Nz
inline DegreeAnglef GetFOV() const;
UInt32 GetRenderMask() const override;
inline Int32 GetRenderOrder() const;
const RenderTarget& GetRenderTarget() override;
const RenderTarget& GetRenderTarget() const;
inline const Vector2f& GetSize() const;
inline const Rectf& GetTargetRegion() const;
ViewerInstance& GetViewerInstance() override;

View File

@ -41,7 +41,7 @@ namespace Nz
void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, std::size_t visibilityHash);
void RegisterMaterialInstance(const MaterialInstance& materialInstance);
FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex);
FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t outputAttachment);
void UnregisterMaterialInstance(const MaterialInstance& materialInstance);

View File

@ -28,6 +28,8 @@ namespace Nz
void FillLightData(void* data) const override;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;
inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const;
inline Color GetColor() const;

View File

@ -18,6 +18,7 @@
#include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Graphics/MaterialPass.hpp>
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/RenderQueue.hpp>
@ -44,7 +45,13 @@ namespace Nz
ForwardFramePipeline(ForwardFramePipeline&&) = delete;
~ForwardFramePipeline();
std::size_t RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask) override;
const std::vector<FramePipelinePass::VisibleRenderable>& FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const override;
void ForEachRegisteredMaterialInstance(FunctionRef<void(const MaterialInstance& materialInstance)> callback) override;
void QueueTransfer(TransferInterface* transfer) override;
std::size_t RegisterLight(const Light* light, UInt32 renderMask) override;
std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) override;
std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) override;
std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override;
@ -79,14 +86,11 @@ namespace Nz
struct LightData
{
std::shared_ptr<Light> light;
std::size_t shadowMapAttachmentIndex;
std::unique_ptr<Camera> camera;
std::unique_ptr<DepthPipelinePass> pass;
std::unique_ptr<LightShadowData> shadowData;
const Light* light;
UInt32 renderMask;
NazaraSlot(Light, OnLightDataInvalided, onLightInvalidated);
NazaraSlot(Light, OnLightTransformInvalided, onLightTransformInvalidated);
NazaraSlot(Light, OnLightShadowCastingChanged, onLightShadowCastingChanged);
};
@ -150,7 +154,7 @@ namespace Nz
std::unordered_map<const RenderTarget*, RenderTargetData> m_renderTargets;
std::unordered_map<MaterialInstance*, MaterialInstanceData> m_materialInstances;
std::vector<ElementRenderer::RenderStates> m_renderStates;
std::vector<FramePipelinePass::VisibleRenderable> m_visibleRenderables;
mutable std::vector<FramePipelinePass::VisibleRenderable> m_visibleRenderables;
std::vector<std::size_t> m_visibleLights;
robin_hood::unordered_set<TransferInterface*> m_transferSet;
BakedFrameGraph m_bakedFrameGraph;
@ -159,7 +163,7 @@ namespace Nz
Bitset<UInt64> m_removedViewerInstances;
Bitset<UInt64> m_removedWorldInstances;
ElementRendererRegistry& m_elementRegistry;
MemoryPool<RenderableData> m_renderablePool;
mutable MemoryPool<RenderableData> m_renderablePool; //< FIXME: has to be mutable because MemoryPool has no const_iterator
MemoryPool<LightData> m_lightPool;
MemoryPool<SkeletonInstanceData> m_skeletonInstances;
MemoryPool<ViewerData> m_viewerPool;

View File

@ -9,10 +9,12 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/FramePipelinePass.hpp>
#include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/SkeletonInstance.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Renderer/DebugDrawer.hpp>
#include <Nazara/Utils/FunctionRef.hpp>
#include <memory>
#include <vector>
@ -21,6 +23,7 @@ namespace Nz
class AbstractViewer;
class InstancedRenderable;
class Light;
class MaterialInstance;
class RenderFrame;
class NAZARA_GRAPHICS_API FramePipeline
@ -31,9 +34,16 @@ namespace Nz
FramePipeline(FramePipeline&&) noexcept = default;
virtual ~FramePipeline();
// TODO: Move RenderQueue handling to proper classes (allowing to reuse them)
virtual const std::vector<FramePipelinePass::VisibleRenderable>& FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const = 0;
virtual void ForEachRegisteredMaterialInstance(FunctionRef<void(const MaterialInstance& materialInstance)> callback) = 0;
inline DebugDrawer& GetDebugDrawer();
virtual std::size_t RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask) = 0;
virtual void QueueTransfer(TransferInterface* transfer) = 0;
virtual std::size_t RegisterLight(const Light* light, UInt32 renderMask) = 0;
virtual std::size_t RegisterRenderable(std::size_t worldInstanceIndex, std::size_t skeletonInstanceIndex, const InstancedRenderable* instancedRenderable, UInt32 renderMask, const Recti& scissorBox) = 0;
virtual std::size_t RegisterSkeleton(SkeletonInstancePtr skeletonInstance) = 0;
virtual std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0;

View File

@ -9,6 +9,7 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector3.hpp>
@ -19,6 +20,8 @@
namespace Nz
{
class CommandBufferBuilder;
class ElementRendererRegistry;
class FramePipeline;
class RenderBuffer;
class RenderFrame;
class Texture;
@ -42,6 +45,8 @@ namespace Nz
inline PixelFormat GetShadowMapFormat() const;
inline UInt32 GetShadowMapSize() const;
virtual std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const = 0;
inline bool IsShadowCaster() const;
inline void UpdateShadowMapFormat(PixelFormat format);

View File

@ -0,0 +1,49 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_GRAPHICS_LIGHTSHADOWDATA_HPP
#define NAZARA_GRAPHICS_LIGHTSHADOWDATA_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
namespace Nz
{
class BakedFrameGraph;
class FrameGraph;
class FramePass;
class MaterialInstance;
class RenderFrame;
class Texture;
class NAZARA_GRAPHICS_API LightShadowData
{
public:
LightShadowData() = default;
LightShadowData(const LightShadowData&) = delete;
LightShadowData(LightShadowData&&) = delete;
virtual ~LightShadowData();
virtual void PrepareRendering(RenderFrame& renderFrame) = 0;
virtual void RegisterMaterialInstance(const MaterialInstance& matInstance) = 0;
virtual void RegisterPassInputs(FramePass& pass) = 0;
virtual void RegisterToFrameGraph(FrameGraph& frameGraph) = 0;
virtual const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const = 0;
virtual void UnregisterMaterialInstance(const MaterialInstance& matInstance) = 0;
LightShadowData& operator=(const LightShadowData&) = delete;
LightShadowData& operator=(LightShadowData&&) = delete;
private:
};
}
#include <Nazara/Graphics/LightShadowData.inl>
#endif // NAZARA_GRAPHICS_LIGHTSHADOWDATA_HPP

View File

@ -0,0 +1,12 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -54,7 +54,6 @@ namespace Nz
std::shared_ptr<GraphicalMesh> m_graphicalMesh;
std::vector<SubMeshData> m_submeshes;
Recti m_scissorBox;
};
}

View File

@ -27,6 +27,8 @@ namespace Nz
void FillLightData(void* data) const override;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;
inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const;
inline Color GetColor() const;

View File

@ -0,0 +1,46 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_GRAPHICS_SHADOWVIEWER_HPP
#define NAZARA_GRAPHICS_SHADOWVIEWER_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp>
namespace Nz
{
class NAZARA_GRAPHICS_API ShadowViewer : public AbstractViewer
{
public:
inline ShadowViewer(const Recti& viewport, UInt32 renderMask);
ShadowViewer(const ShadowViewer&) = delete;
ShadowViewer(ShadowViewer&&) = delete;
~ShadowViewer() = default;
const Color& GetClearColor() const override;
UInt32 GetRenderMask() const override;
const RenderTarget& GetRenderTarget() const override;
ViewerInstance& GetViewerInstance() override;
const ViewerInstance& GetViewerInstance() const override;
const Recti& GetViewport() const override;
inline void UpdateRenderMask(UInt32 renderMask);
inline void UpdateViewport(const Recti& viewport);
ShadowViewer& operator=(const ShadowViewer&) = delete;
ShadowViewer& operator=(ShadowViewer&&) = delete;
private:
Recti m_viewport;
ViewerInstance m_viewerInstance;
UInt32 m_renderMask;
};
}
#include <Nazara/Graphics/ShadowViewer.inl>
#endif // NAZARA_GRAPHICS_SHADOWVIEWER_HPP

View File

@ -0,0 +1,28 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/ShadowViewer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline ShadowViewer::ShadowViewer(const Recti& viewport, UInt32 renderMask) :
m_renderMask(renderMask)
{
UpdateViewport(viewport);
}
inline void ShadowViewer::UpdateRenderMask(UInt32 renderMask)
{
m_renderMask = renderMask;
}
inline void ShadowViewer::UpdateViewport(const Recti& viewport)
{
m_viewport = viewport;
m_viewerInstance.UpdateTargetSize({ float(m_viewport.width), float(m_viewport.height) });
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -38,6 +38,8 @@ namespace Nz
inline const Quaternionf& GetRotation() const;
inline float GetRadius() const;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;
inline void UpdateAmbientFactor(float factor);
inline void UpdateColor(Color color);
inline void UpdateDiffuseFactor(float factor);

View File

@ -0,0 +1,56 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP
#define NAZARA_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/DepthPipelinePass.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Graphics/ShadowViewer.hpp>
namespace Nz
{
class FramePipeline;
class SpotLight;
class NAZARA_GRAPHICS_API SpotLightShadowData : public LightShadowData
{
public:
SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light);
SpotLightShadowData(const SpotLightShadowData&) = delete;
SpotLightShadowData(SpotLightShadowData&&) = delete;
~SpotLightShadowData() = default;
void PrepareRendering(RenderFrame& renderFrame) override;
void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
void RegisterPassInputs(FramePass& pass) override;
void RegisterToFrameGraph(FrameGraph& frameGraph) override;
const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const override;
void UnregisterMaterialInstance(const MaterialInstance& matInstance) override;
SpotLightShadowData& operator=(const SpotLightShadowData&) = delete;
SpotLightShadowData& operator=(SpotLightShadowData&&) = delete;
private:
NazaraSlot(Light, OnLightShadowMapSettingChange, m_onLightShadowMapSettingChange);
NazaraSlot(Light, OnLightTransformInvalided, m_onLightTransformInvalidated);
std::optional<DepthPipelinePass> m_depthPass;
std::size_t m_attachmentIndex;
FramePipeline& m_pipeline;
const SpotLight& m_light;
ShadowViewer m_viewer;
};
}
#include <Nazara/Graphics/SpotLightShadowData.inl>
#endif // NAZARA_GRAPHICS_SPOTLIGHTSHADOWDATA_HPP

View File

@ -0,0 +1,12 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/SpotLightShadowData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -18,7 +18,7 @@ namespace Nz
return m_renderMask;
}
const RenderTarget& Camera::GetRenderTarget()
const RenderTarget& Camera::GetRenderTarget() const
{
if (!m_renderTarget)
throw std::runtime_error("no rendertarget set");

View File

@ -129,10 +129,10 @@ namespace Nz
it->second.usedCount++;
}
FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex)
FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t outputAttachment)
{
FramePass& depthPrepass = frameGraph.AddPass(m_passName);
depthPrepass.SetDepthStencilOutput(depthBufferIndex);
depthPrepass.SetDepthStencilOutput(outputAttachment);
depthPrepass.SetDepthStencilClear(1.f, 0);
depthPrepass.SetExecutionCallback([&]()

View File

@ -30,6 +30,11 @@ namespace Nz
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f);
}
std::unique_ptr<LightShadowData> DirectionalLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
{
return nullptr; //< TODO
}
void DirectionalLight::UpdateTransform(const Vector3f& /*position*/, const Quaternionf& rotation, const Vector3f& /*scale*/)
{
UpdateRotation(rotation);

View File

@ -44,11 +44,67 @@ namespace Nz
m_viewerPool.Clear();
}
std::size_t ForwardFramePipeline::RegisterLight(std::shared_ptr<Light> light, UInt32 renderMask)
const std::vector<Nz::FramePipelinePass::VisibleRenderable>& ForwardFramePipeline::FrustumCull(const Frustumf& frustum, UInt32 mask, std::size_t& visibilityHash) const
{
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
return currentHash * 23 + newHash;
};
m_visibleRenderables.clear();
for (const RenderableData& renderableData : m_renderablePool)
{
if ((mask & renderableData.renderMask) == 0)
continue;
const WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance;
// Get global AABB
BoundingVolumef boundingVolume(renderableData.renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume))
continue;
auto& visibleRenderable = m_visibleRenderables.emplace_back();
visibleRenderable.instancedRenderable = renderableData.renderable;
visibleRenderable.scissorBox = renderableData.scissorBox;
visibleRenderable.worldInstance = worldInstance.get();
if (renderableData.skeletonInstanceIndex != NoSkeletonInstance)
visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get();
else
visibleRenderable.skeletonInstance = nullptr;
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(&renderableData));
}
return m_visibleRenderables;
}
void ForwardFramePipeline::ForEachRegisteredMaterialInstance(FunctionRef<void(const MaterialInstance& materialInstance)> callback)
{
for (RenderableData& renderable : m_renderablePool)
{
std::size_t matCount = renderable.renderable->GetMaterialCount();
for (std::size_t j = 0; j < matCount; ++j)
{
if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get())
callback(*mat);
}
}
}
void ForwardFramePipeline::QueueTransfer(TransferInterface* transfer)
{
m_transferSet.insert(transfer);
}
std::size_t ForwardFramePipeline::RegisterLight(const Light* light, UInt32 renderMask)
{
std::size_t lightIndex;
LightData* lightData = m_lightPool.Allocate(lightIndex);
lightData->light = std::move(light);
lightData->light = light;
lightData->renderMask = renderMask;
lightData->onLightInvalidated.Connect(lightData->light->OnLightDataInvalided, [=](Light*)
{
@ -65,11 +121,14 @@ namespace Nz
lightData->onLightShadowCastingChanged.Connect(lightData->light->OnLightShadowCastingChanged, [=](Light* light, bool isCastingShadows)
{
if (isCastingShadows)
{
m_shadowCastingLights.UnboundedSet(lightIndex);
lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry);
}
else
{
m_shadowCastingLights.Reset(lightIndex);
lightData->pass.reset();
lightData->shadowData.reset();
}
m_rebuildFrameGraph = true;
@ -78,6 +137,7 @@ namespace Nz
if (lightData->light->IsShadowCaster())
{
m_shadowCastingLights.UnboundedSet(lightIndex);
lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry);
m_rebuildFrameGraph = true;
}
@ -215,7 +275,7 @@ namespace Nz
const Light* ForwardFramePipeline::RetrieveLight(std::size_t lightIndex) const
{
return m_lightPool.RetrieveFromIndex(lightIndex)->light.get();
return m_lightPool.RetrieveFromIndex(lightIndex)->light;
}
const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex) const
@ -223,8 +283,7 @@ namespace Nz
if (!m_shadowCastingLights.UnboundedTest(lightIndex))
return nullptr;
std::size_t shadowmapIndex = m_lightPool.RetrieveFromIndex(lightIndex)->shadowMapAttachmentIndex;
return m_bakedFrameGraph.GetAttachmentTexture(shadowmapIndex).get();
return m_lightPool.RetrieveFromIndex(lightIndex)->shadowData->RetrieveLightShadowmap(m_bakedFrameGraph);
}
void ForwardFramePipeline::Render(RenderFrame& renderFrame)
@ -279,51 +338,11 @@ namespace Nz
builder.EndDebugRegion();
}, QueueType::Transfer);
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
return currentHash * 23 + newHash;
};
// Shadow map handling
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
const Matrix4f& viewProjMatrix = lightData->camera->GetViewerInstance().GetViewProjMatrix();
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
std::size_t visibilityHash = 5U;
m_visibleRenderables.clear();
for (const RenderableData& renderableData : m_renderablePool)
{
if ((lightData->renderMask & renderableData.renderMask) == 0)
continue;
WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance;
// Get global AABB
BoundingVolumef boundingVolume(renderableData.renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume))
continue;
auto& visibleRenderable = m_visibleRenderables.emplace_back();
visibleRenderable.instancedRenderable = renderableData.renderable;
visibleRenderable.scissorBox = renderableData.scissorBox;
visibleRenderable.worldInstance = worldInstance.get();
if (renderableData.skeletonInstanceIndex != NoSkeletonInstance)
visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get();
else
visibleRenderable.skeletonInstance = nullptr;
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(&renderableData));
}
lightData->pass->Prepare(renderFrame, frustum, m_visibleRenderables, visibilityHash);
lightData->shadowData->PrepareRendering(renderFrame);
}
// Render queues handling
@ -335,36 +354,8 @@ namespace Nz
const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix();
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
std::size_t visibilityHash = 5U;
m_visibleRenderables.clear();
for (const RenderableData& renderableData : m_renderablePool)
{
if ((renderMask & renderableData.renderMask) == 0)
continue;
WorldInstancePtr& worldInstance = m_worldInstances.RetrieveFromIndex(renderableData.worldInstanceIndex)->worldInstance;
// Get global AABB
BoundingVolumef boundingVolume(renderableData.renderable->GetAABB());
boundingVolume.Update(worldInstance->GetWorldMatrix());
if (!frustum.Contains(boundingVolume))
continue;
auto& visibleRenderable = m_visibleRenderables.emplace_back();
visibleRenderable.instancedRenderable = renderableData.renderable;
visibleRenderable.scissorBox = renderableData.scissorBox;
visibleRenderable.worldInstance = worldInstance.get();
if (renderableData.skeletonInstanceIndex != NoSkeletonInstance)
visibleRenderable.skeletonInstance = m_skeletonInstances.RetrieveFromIndex(renderableData.skeletonInstanceIndex)->skeleton.get();
else
visibleRenderable.skeletonInstance = nullptr;
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(&renderableData));
}
std::size_t visibilityHash = 5;
const auto& visibleRenderables = FrustumCull(frustum, renderMask, visibilityHash);
// Lights update don't trigger a rebuild of the depth pre-pass
std::size_t depthVisibilityHash = visibilityHash;
@ -381,14 +372,20 @@ namespace Nz
if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume))
{
m_visibleLights.push_back(lightIndex);
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(lightData.light.get()));
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
return currentHash * 23 + newHash;
};
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(lightData.light));
}
}
if (viewerData.depthPrepass)
viewerData.depthPrepass->Prepare(renderFrame, frustum, m_visibleRenderables, depthVisibilityHash);
viewerData.depthPrepass->Prepare(renderFrame, frustum, visibleRenderables, depthVisibilityHash);
viewerData.forwardPass->Prepare(renderFrame, frustum, m_visibleRenderables, m_visibleLights, visibilityHash);
viewerData.forwardPass->Prepare(renderFrame, frustum, visibleRenderables, m_visibleLights, visibilityHash);
viewerData.debugDrawPass->Prepare(renderFrame);
}
@ -571,60 +568,7 @@ namespace Nz
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
assert(lightData->light->GetLightType() == UnderlyingCast(BasicLightType::Spot));
SpotLight& spotLight = SafeCast<SpotLight&>(*lightData->light);
PixelFormat shadowMapFormat = lightData->light->GetShadowMapFormat();
UInt32 shadowMapSize = lightData->light->GetShadowMapSize();
lightData->shadowMapAttachmentIndex = frameGraph.AddAttachment({
"Shadowmap",
shadowMapFormat,
FramePassAttachmentSize::Fixed,
shadowMapSize, shadowMapSize,
});
if (!lightData->camera)
{
lightData->camera = std::make_unique<Camera>(nullptr);
ViewerInstance& viewerInstance = lightData->camera->GetViewerInstance();
viewerInstance.OnTransferRequired.Connect([this](TransferInterface* transferInterface)
{
m_transferSet.insert(transferInterface);
});
lightData->camera->UpdateFOV(spotLight.GetOuterAngle() * 2.f);
lightData->camera->UpdateZNear(0.01f);
lightData->camera->UpdateZFar(spotLight.GetRadius());
lightData->camera->UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
lightData->onLightTransformInvalidated.Connect(lightData->light->OnLightTransformInvalided, [lightData](Light* light)
{
SpotLight& spotLight = SafeCast<SpotLight&>(*light);
ViewerInstance& viewerInstance = lightData->camera->GetViewerInstance();
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(spotLight.GetPosition(), spotLight.GetRotation()));
});
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(spotLight.GetPosition(), spotLight.GetRotation()));
}
if (!lightData->pass)
{
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass");
lightData->pass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, lightData->camera.get(), shadowPassIndex, "Spot shadowmap");
for (RenderableData& renderable : m_renderablePool)
{
std::size_t matCount = renderable.renderable->GetMaterialCount();
for (std::size_t j = 0; j < matCount; ++j)
{
if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get())
lightData->pass->RegisterMaterialInstance(*mat);
}
}
lightData->pass->RegisterToFrameGraph(frameGraph, lightData->shadowMapAttachmentIndex);
}
lightData->shadowData->RegisterToFrameGraph(frameGraph);
}
for (auto& viewerData : m_viewerPool)
@ -648,7 +592,7 @@ namespace Nz
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
forwardPass.AddInput(lightData->shadowMapAttachmentIndex);
lightData->shadowData->RegisterPassInputs(forwardPass);
}
viewerData.debugDrawPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.debugColorAttachment);

View File

@ -165,8 +165,8 @@ namespace Nz
perElementData.lightCount = lightCount;
perElementData.lightUniformBuffer = lightUboView;
for (std::size_t i = 0; i < lightCount; ++i)
perElementData.shadowMaps[i] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[i].lightIndex);
for (std::size_t j = 0; j < lightCount; ++j)
perElementData.shadowMaps[j] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[j].lightIndex);
m_lightPerRenderElement.emplace(element, perElementData);
}
@ -240,18 +240,18 @@ namespace Nz
auto& renderStates = m_renderStates.emplace_back();
renderStates.lightData = lightData.lightUniformBuffer;
for (std::size_t i = 0; i < lightData.lightCount; ++i)
for (std::size_t j = 0; j < lightData.lightCount; ++j)
{
const Texture* texture = lightData.shadowMaps[i];
const Texture* texture = lightData.shadowMaps[j];
if (!texture)
continue;
if (texture->GetType() == ImageType::E2D)
renderStates.shadowMaps2D[i] = texture;
renderStates.shadowMaps2D[j] = texture;
else
{
assert(texture->GetType() == ImageType::Cubemap);
renderStates.shadowMapsCube[i] = texture;
renderStates.shadowMapsCube[j] = texture;
}
}
}

View File

@ -0,0 +1,11 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
LightShadowData::~LightShadowData() = default;
}

View File

@ -30,6 +30,11 @@ namespace Nz
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f);
}
std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
{
return nullptr; //< TODO
}
void PointLight::UpdateTransform(const Vector3f& position, const Quaternionf& /*rotation*/, const Vector3f& /*scale*/)
{
UpdatePosition(position);

View File

@ -0,0 +1,39 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/ShadowViewer.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
const Color& ShadowViewer::GetClearColor() const
{
throw std::runtime_error("no clear color");
}
UInt32 ShadowViewer::GetRenderMask() const
{
return m_renderMask;
}
const RenderTarget& ShadowViewer::GetRenderTarget() const
{
throw std::runtime_error("no render target");
}
ViewerInstance& ShadowViewer::GetViewerInstance()
{
return m_viewerInstance;
}
const ViewerInstance& ShadowViewer::GetViewerInstance() const
{
return m_viewerInstance;
}
const Recti& ShadowViewer::GetViewport() const
{
return m_viewport;
}
}

View File

@ -6,6 +6,7 @@
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Graphics/SpotLightShadowData.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Math/Vector4.hpp>
@ -33,6 +34,11 @@ namespace Nz
AccessByOffset<Matrix4f&>(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix;
}
std::unique_ptr<LightShadowData> SpotLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
{
return std::make_unique<SpotLightShadowData>(pipeline, elementRegistry, *this);
}
void SpotLight::UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& /*scale*/)
{
m_position = position; //< don't call UpdatePosition to prevent double update

View File

@ -0,0 +1,99 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/SpotLightShadowData.hpp>
#include <Nazara/Graphics/BakedFrameGraph.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/SpotLight.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
SpotLightShadowData::SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light) :
m_pipeline(pipeline),
m_light(light),
m_viewer(Recti(0, 0, 1, 1), 0xFFFFFFFF)
{
UInt32 shadowMapSize = light.GetShadowMapSize();
m_viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
ViewerInstance& viewerInstance = m_viewer.GetViewerInstance();
viewerInstance.UpdateProjectionMatrix(Matrix4f::Perspective(m_light.GetOuterAngle() * 2.f, 1.f, 0.01f, m_light.GetRadius()));
m_onLightShadowMapSettingChange.Connect(m_light.OnLightShadowMapSettingChange, [this](Light* /*light*/, PixelFormat /*newPixelFormat*/, UInt32 newSize)
{
m_viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(newSize), SafeCast<int>(newSize)));
});
m_onLightTransformInvalidated.Connect(m_light.OnLightTransformInvalided, [this]([[maybe_unused]] Light* light)
{
assert(&m_light == light);
ViewerInstance& viewerInstance = m_viewer.GetViewerInstance();
viewerInstance.UpdateEyePosition(m_light.GetPosition());
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(m_light.GetPosition(), m_light.GetRotation()));
m_pipeline.QueueTransfer(&viewerInstance);
});
viewerInstance.UpdateEyePosition(m_light.GetPosition());
viewerInstance.UpdateViewMatrix(Nz::Matrix4f::TransformInverse(m_light.GetPosition(), m_light.GetRotation()));
m_pipeline.QueueTransfer(&viewerInstance);
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass");
m_depthPass.emplace(m_pipeline, elementRegistry, &m_viewer, shadowPassIndex, "Spotlight shadow mapping");
m_pipeline.ForEachRegisteredMaterialInstance([this](const MaterialInstance& matInstance)
{
m_depthPass->RegisterMaterialInstance(matInstance);
});
}
void SpotLightShadowData::PrepareRendering(RenderFrame& renderFrame)
{
const Matrix4f& viewProjMatrix = m_viewer.GetViewerInstance().GetViewProjMatrix();
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
std::size_t visibilityHash = 5U;
const auto& visibleRenderables = m_pipeline.FrustumCull(frustum, 0xFFFFFFFF, visibilityHash);
m_depthPass->Prepare(renderFrame, frustum, visibleRenderables, visibilityHash);
}
void SpotLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance)
{
m_depthPass->RegisterMaterialInstance(matInstance);
}
void SpotLightShadowData::RegisterPassInputs(FramePass& pass)
{
pass.AddInput(m_attachmentIndex);
}
void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph)
{
UInt32 shadowMapSize = m_light.GetShadowMapSize();
m_attachmentIndex = frameGraph.AddAttachment({
"Shadowmap",
m_light.GetShadowMapFormat(),
FramePassAttachmentSize::Fixed,
shadowMapSize, shadowMapSize,
});
m_depthPass->RegisterToFrameGraph(frameGraph, m_attachmentIndex);
}
const Nz::Texture* SpotLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const
{
return bakedGraph.GetAttachmentTexture(m_attachmentIndex).get();
}
void SpotLightShadowData::UnregisterMaterialInstance(const MaterialInstance& matInstance)
{
m_depthPass->UnregisterMaterialInstance(matInstance);
}
}

View File

@ -378,7 +378,7 @@ namespace Nz
return;
const auto& lightEntry = light->GetLightEntry(lightIndex);
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask);
});
lightEntity->onLightDetach.Connect(entityLight.OnLightDetach, [this, lightEntity](LightComponent* light, std::size_t lightIndex)
@ -413,7 +413,7 @@ namespace Nz
if (!lightEntry.light)
continue;
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
lightEntity->lightIndices[lightIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask);
}
}
@ -515,7 +515,7 @@ namespace Nz
if (!lightEntry.light)
continue;
lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light, lightEntry.renderMask);
lightEntity->lightIndices[renderableIndex] = m_pipeline->RegisterLight(lightEntry.light.get(), lightEntry.renderMask);
}
}
m_newlyVisibleGfxEntities.clear();