Graphics: Rework shadowing (add cascaded shadow mapping)

- Add support for per-viewer shadows
- Add cascaded shadow mapping for directional lights (wip)
- Rework the way lights are sent to the shaders (they are now selected once per viewer)
- Fixes PointLight shadow mapping (using a dedicated pass)
- Lights out of frustum for every viewers are no longer processed (wip)
This commit is contained in:
SirLynix 2023-09-06 13:20:52 +02:00 committed by Jérôme Leclercq
parent a08850946a
commit 9aebb4f745
41 changed files with 1320 additions and 576 deletions

View File

@ -61,7 +61,6 @@ namespace Nz
std::size_t m_lastVisibilityHash; std::size_t m_lastVisibilityHash;
std::string m_passName; std::string m_passName;
std::vector<std::unique_ptr<ElementRendererData>> m_elementRendererData; std::vector<std::unique_ptr<ElementRendererData>> m_elementRendererData;
std::vector<ElementRenderer::RenderStates> m_renderStates;
std::vector<RenderElementOwner> m_renderElements; std::vector<RenderElementOwner> m_renderElements;
std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances; std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances;
RenderQueue<const RenderElement*> m_renderQueue; RenderQueue<const RenderElement*> m_renderQueue;

View File

@ -24,9 +24,9 @@ namespace Nz
DirectionalLight(DirectionalLight&&) noexcept = default; DirectionalLight(DirectionalLight&&) noexcept = default;
~DirectionalLight() = default; ~DirectionalLight() = default;
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const Frustumf& viewerFrustum) const override;
void FillLightData(void* data) const override; bool FrustumCull(const Frustumf& viewerFrustum) const override;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;

View File

@ -0,0 +1,94 @@
// Copyright (C) 2023 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_DIRECTIONALLIGHTSHADOWDATA_HPP
#define NAZARA_GRAPHICS_DIRECTIONALLIGHTSHADOWDATA_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/DepthPipelinePass.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Graphics/ShadowViewer.hpp>
#include <NazaraUtils/FixedVector.hpp>
#include <NazaraUtils/SparsePtr.hpp>
#include <unordered_map>
namespace Nz
{
class FramePipeline;
class DirectionalLight;
class NAZARA_GRAPHICS_API DirectionalLightShadowData : public LightShadowData
{
public:
DirectionalLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const DirectionalLight& light, std::size_t cascadeCount);
DirectionalLightShadowData(const DirectionalLightShadowData&) = delete;
DirectionalLightShadowData(DirectionalLightShadowData&&) = delete;
~DirectionalLightShadowData() = default;
inline void EnableShadowStabilization(bool enable);
inline std::size_t GetCascadeCount() const;
inline void GetCascadeData(const AbstractViewer* viewer, SparsePtr<float> distance, SparsePtr<Matrix4f> viewProjMatrix) const;
inline bool IsShadowStabilization() const;
void PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer) override;
void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) override;
void RegisterToFrameGraph(FrameGraph& frameGraph, const AbstractViewer* viewer) override;
void RegisterViewer(const AbstractViewer* viewer) override;
const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* viewer) const override;
void UnregisterMaterialInstance(const MaterialInstance& matInstance) override;
void UnregisterViewer(const AbstractViewer* viewer) override;
DirectionalLightShadowData& operator=(const DirectionalLightShadowData&) = delete;
DirectionalLightShadowData& operator=(DirectionalLightShadowData&&) = delete;
private:
struct CascadeData;
template<typename F> void ForEachCascade(F&& callback);
void ComputeLightView(CascadeData& cascade, const Frustumf& cascadeFrustum, float cascadeDist);
void StabilizeShadows(CascadeData& cascade);
NazaraSlot(Light, OnLightShadowMapSettingChange, m_onLightShadowMapSettingChange);
NazaraSlot(Light, OnLightTransformInvalided, m_onLightTransformInvalidated);
struct CascadeData
{
std::optional<DepthPipelinePass> depthPass;
std::size_t attachmentIndex;
Matrix4f viewProjMatrix;
ShadowViewer viewer;
float distance;
};
struct PerViewerData
{
FixedVector<CascadeData, 8> cascades;
std::size_t textureArrayAttachmentIndex;
};
std::size_t m_cascadeCount;
std::unordered_map<const AbstractViewer*, std::unique_ptr<PerViewerData>> m_viewerData;
ElementRendererRegistry& m_elementRegistry;
FramePipeline& m_pipeline;
const DirectionalLight& m_light;
bool m_isShadowStabilizationEnabled;
float m_invTexelScale;
float m_texelScale;
};
}
#include <Nazara/Graphics/DirectionalLightShadowData.inl>
#endif // NAZARA_GRAPHICS_DIRECTIONALLIGHTSHADOWDATA_HPP

View File

@ -0,0 +1,42 @@
// Copyright (C) 2023 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 <cassert>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
inline void DirectionalLightShadowData::EnableShadowStabilization(bool enable)
{
m_isShadowStabilizationEnabled = enable;
}
inline std::size_t DirectionalLightShadowData::GetCascadeCount() const
{
return m_cascadeCount;
}
inline void DirectionalLightShadowData::GetCascadeData(const AbstractViewer* viewer, SparsePtr<float> distance, SparsePtr<Matrix4f> viewProjMatrix) const
{
assert(viewer);
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
for (const auto& cascadeData : viewerData.cascades)
{
if (distance)
*distance++ = cascadeData.distance;
if (viewProjMatrix)
*viewProjMatrix++ = cascadeData.viewProjMatrix;
}
}
inline bool DirectionalLightShadowData::IsShadowStabilization() const
{
return m_isShadowStabilizationEnabled;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -14,6 +14,7 @@
#include <Nazara/Graphics/PredefinedShaderStructs.hpp> #include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Graphics/RenderElementPool.hpp> #include <Nazara/Graphics/RenderElementPool.hpp>
#include <Nazara/Renderer/RenderBufferView.hpp> #include <Nazara/Renderer/RenderBufferView.hpp>
#include <NazaraUtils/SparsePtr.hpp>
#include <array> #include <array>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -24,6 +25,7 @@ namespace Nz
class CommandBufferBuilder; class CommandBufferBuilder;
class RenderElement; class RenderElement;
class RenderFrame; class RenderFrame;
class Texture;
class ViewerInstance; class ViewerInstance;
struct ElementRendererData; struct ElementRendererData;
@ -38,7 +40,7 @@ namespace Nz
virtual RenderElementPoolBase& GetPool() = 0; virtual RenderElementPoolBase& GetPool() = 0;
virtual std::unique_ptr<ElementRendererData> InstanciateData() = 0; virtual std::unique_ptr<ElementRendererData> InstanciateData() = 0;
virtual void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates); virtual void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, SparsePtr<const RenderStates> renderStates);
virtual void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData); virtual void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData);
virtual void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) = 0; virtual void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) = 0;
virtual void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame); virtual void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame);
@ -47,12 +49,14 @@ namespace Nz
{ {
RenderStates() RenderStates()
{ {
shadowMaps2D.fill(nullptr); shadowMapsDirectional.fill(nullptr);
shadowMapsCube.fill(nullptr); shadowMapsPoint.fill(nullptr);
shadowMapsSpot.fill(nullptr);
} }
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMaps2D; std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsDirectional;
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsCube; std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsPoint;
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsSpot;
RenderBufferView lightData; RenderBufferView lightData;
}; };
}; };

View File

@ -125,8 +125,9 @@ namespace Nz
InstanceDataUbo, InstanceDataUbo,
LightDataUbo, LightDataUbo,
OverlayTexture, OverlayTexture,
Shadowmap2D, ShadowmapDirectional,
ShadowmapCube, ShadowmapPoint,
ShadowmapSpot,
SkeletalDataUbo, SkeletalDataUbo,
ViewerDataUbo, ViewerDataUbo,

View File

@ -13,7 +13,6 @@
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/DebugDrawPipelinePass.hpp> #include <Nazara/Graphics/DebugDrawPipelinePass.hpp>
#include <Nazara/Graphics/DepthPipelinePass.hpp> #include <Nazara/Graphics/DepthPipelinePass.hpp>
#include <Nazara/Graphics/ElementRenderer.hpp>
#include <Nazara/Graphics/ForwardPipelinePass.hpp> #include <Nazara/Graphics/ForwardPipelinePass.hpp>
#include <Nazara/Graphics/FramePipeline.hpp> #include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp> #include <Nazara/Graphics/InstancedRenderable.hpp>
@ -35,6 +34,7 @@
namespace Nz namespace Nz
{ {
class LightShadowData;
class RenderFrame; class RenderFrame;
class RenderTarget; class RenderTarget;
@ -59,7 +59,8 @@ namespace Nz
std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) override; std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) override;
const Light* RetrieveLight(std::size_t lightIndex) const override; const Light* RetrieveLight(std::size_t lightIndex) const override;
const Texture* RetrieveLightShadowmap(std::size_t lightIndex) const override; const LightShadowData* RetrieveLightShadowData(std::size_t lightIndex) const override;
const Texture* RetrieveLightShadowmap(std::size_t lightIndex, const AbstractViewer* viewer) const override;
void Render(RenderFrame& renderFrame) override; void Render(RenderFrame& renderFrame) override;
@ -132,6 +133,12 @@ namespace Nz
struct ViewerData struct ViewerData
{ {
struct FrameData
{
Bitset<UInt64> visibleLights;
Frustumf frustum;
};
std::size_t finalColorAttachment; std::size_t finalColorAttachment;
std::size_t forwardColorAttachment; std::size_t forwardColorAttachment;
std::size_t debugColorAttachment; std::size_t debugColorAttachment;
@ -145,6 +152,8 @@ namespace Nz
RenderQueueRegistry forwardRegistry; RenderQueueRegistry forwardRegistry;
RenderQueue<RenderElement*> forwardRenderQueue; RenderQueue<RenderElement*> forwardRenderQueue;
ShaderBindingPtr blitShaderBinding; ShaderBindingPtr blitShaderBinding;
FrameData frame;
bool pendingDestruction = false;
NazaraSlot(TransferInterface, OnTransferRequired, onTransferRequired); NazaraSlot(TransferInterface, OnTransferRequired, onTransferRequired);
}; };
@ -158,17 +167,17 @@ namespace Nz
std::unordered_map<const RenderTarget*, RenderTargetData> m_renderTargets; std::unordered_map<const RenderTarget*, RenderTargetData> m_renderTargets;
std::unordered_map<MaterialInstance*, MaterialInstanceData> m_materialInstances; std::unordered_map<MaterialInstance*, MaterialInstanceData> m_materialInstances;
std::vector<ElementRenderer::RenderStates> m_renderStates;
mutable 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; robin_hood::unordered_set<TransferInterface*> m_transferSet;
BakedFrameGraph m_bakedFrameGraph; BakedFrameGraph m_bakedFrameGraph;
Bitset<UInt64> m_shadowCastingLights; Bitset<UInt64> m_activeLights;
Bitset<UInt64> m_removedSkeletonInstances; Bitset<UInt64> m_removedSkeletonInstances;
Bitset<UInt64> m_removedViewerInstances; Bitset<UInt64> m_removedViewerInstances;
Bitset<UInt64> m_removedWorldInstances; Bitset<UInt64> m_removedWorldInstances;
Bitset<UInt64> m_shadowCastingLights;
Bitset<UInt64> m_visibleShadowCastingLights;
ElementRendererRegistry& m_elementRegistry; ElementRendererRegistry& m_elementRegistry;
mutable MemoryPool<RenderableData> m_renderablePool; //< FIXME: has to be mutable because MemoryPool has no const_iterator MemoryPool<RenderableData> m_renderablePool;
MemoryPool<LightData> m_lightPool; MemoryPool<LightData> m_lightPool;
MemoryPool<SkeletonInstanceData> m_skeletonInstances; MemoryPool<SkeletonInstanceData> m_skeletonInstances;
MemoryPool<ViewerData> m_viewerPool; MemoryPool<ViewerData> m_viewerPool;

View File

@ -11,9 +11,7 @@
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/ElementRenderer.hpp> #include <Nazara/Graphics/ElementRenderer.hpp>
#include <Nazara/Graphics/FramePipelinePass.hpp> #include <Nazara/Graphics/FramePipelinePass.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/MaterialInstance.hpp> #include <Nazara/Graphics/MaterialInstance.hpp>
#include <Nazara/Graphics/MaterialPass.hpp>
#include <Nazara/Graphics/RenderElement.hpp> #include <Nazara/Graphics/RenderElement.hpp>
#include <Nazara/Graphics/RenderElementOwner.hpp> #include <Nazara/Graphics/RenderElementOwner.hpp>
#include <Nazara/Graphics/RenderQueue.hpp> #include <Nazara/Graphics/RenderQueue.hpp>
@ -24,13 +22,16 @@
namespace Nz namespace Nz
{ {
class AbstractViewer; class AbstractViewer;
class DirectionalLight;
class ElementRendererRegistry; class ElementRendererRegistry;
class FrameGraph; class FrameGraph;
class FramePass; class FramePass;
class FramePipeline; class FramePipeline;
class Light; class Light;
class PointLight;
class SpotLight;
class NAZARA_GRAPHICS_API ForwardPipelinePass : public FramePipelinePass class NAZARA_GRAPHICS_API ForwardPipelinePass : public FramePipelinePass, TransferInterface
{ {
public: public:
ForwardPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer); ForwardPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer);
@ -41,7 +42,7 @@ namespace Nz
inline void InvalidateCommandBuffers(); inline void InvalidateCommandBuffers();
inline void InvalidateElements(); inline void InvalidateElements();
void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const std::vector<std::size_t>& visibleLights, std::size_t visibilityHash); void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const Bitset<UInt64>& visibleLights, std::size_t visibilityHash);
void RegisterMaterialInstance(const MaterialInstance& material); void RegisterMaterialInstance(const MaterialInstance& material);
FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass); FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass);
@ -51,9 +52,14 @@ namespace Nz
ForwardPipelinePass& operator=(const ForwardPipelinePass&) = delete; ForwardPipelinePass& operator=(const ForwardPipelinePass&) = delete;
ForwardPipelinePass& operator=(ForwardPipelinePass&&) = delete; ForwardPipelinePass& operator=(ForwardPipelinePass&&) = delete;
static constexpr std::size_t MaxLightCountPerDraw = 3;
private: private:
void OnTransfer(RenderFrame& renderFrame, CommandBufferBuilder& builder) override;
void PrepareDirectionalLights(void* lightMemory);
void PreparePointLights(void* lightMemory);
void PrepareSpotLights(void* lightMemory);
void PrepareLights(RenderFrame& renderFrame, const Frustumf& frustum, const Bitset<UInt64>& visibleLights);
struct MaterialPassEntry struct MaterialPassEntry
{ {
std::size_t usedCount = 1; std::size_t usedCount = 1;
@ -62,55 +68,30 @@ namespace Nz
NazaraSlot(MaterialInstance, OnMaterialInstanceShaderBindingInvalidated, onMaterialInstanceShaderBindingInvalidated); NazaraSlot(MaterialInstance, OnMaterialInstanceShaderBindingInvalidated, onMaterialInstanceShaderBindingInvalidated);
}; };
using LightKey = std::array<const Light*, MaxLightCountPerDraw>; template<typename T>
struct LightKeyHasher
{
inline std::size_t operator()(const LightKey& lightKey) const;
};
struct LightDataUbo
{
std::shared_ptr<RenderBuffer> renderBuffer;
std::size_t offset = 0;
UploadPool::Allocation* allocation = nullptr;
};
struct LightPerElementData
{
RenderBufferView lightUniformBuffer;
std::array<const Texture*, MaxLightCountPerDraw> shadowMaps;
std::size_t lightCount;
};
struct LightUboPool
{
std::vector<std::shared_ptr<RenderBuffer>> lightUboBuffers;
};
struct RenderableLight struct RenderableLight
{ {
const Light* light; const T* light;
std::size_t lightIndex; std::size_t lightIndex;
float contributionScore; float contributionScore;
}; };
std::size_t m_forwardPassIndex; std::size_t m_forwardPassIndex;
std::size_t m_lastVisibilityHash; std::size_t m_lastVisibilityHash;
std::shared_ptr<LightUboPool> m_lightUboPool; std::shared_ptr<RenderBuffer> m_lightDataBuffer;
std::vector<std::unique_ptr<ElementRendererData>> m_elementRendererData; std::vector<std::unique_ptr<ElementRendererData>> m_elementRendererData;
std::vector<ElementRenderer::RenderStates> m_renderStates;
std::vector<RenderElementOwner> m_renderElements; std::vector<RenderElementOwner> m_renderElements;
std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances; std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances;
std::unordered_map<const RenderElement*, LightPerElementData> m_lightPerRenderElement; std::vector<RenderableLight<DirectionalLight>> m_directionalLights;
std::unordered_map<LightKey, RenderBufferView, LightKeyHasher> m_lightBufferPerLights; std::vector<RenderableLight<PointLight>> m_pointLights;
std::vector<LightDataUbo> m_lightDataBuffers; std::vector<RenderableLight<SpotLight>> m_spotLights;
std::vector<RenderableLight> m_renderableLights; ElementRenderer::RenderStates m_renderState;
RenderQueue<const RenderElement*> m_renderQueue; RenderQueue<const RenderElement*> m_renderQueue;
RenderQueueRegistry m_renderQueueRegistry; RenderQueueRegistry m_renderQueueRegistry;
AbstractViewer* m_viewer; AbstractViewer* m_viewer;
ElementRendererRegistry& m_elementRegistry; ElementRendererRegistry& m_elementRegistry;
FramePipeline& m_pipeline; FramePipeline& m_pipeline;
UploadPool::Allocation* m_pendingLightUploadAllocation;
bool m_rebuildCommandBuffer; bool m_rebuildCommandBuffer;
bool m_rebuildElements; bool m_rebuildElements;
}; };

View File

@ -15,21 +15,6 @@ namespace Nz
{ {
m_rebuildElements = true; m_rebuildElements = true;
} }
inline std::size_t ForwardPipelinePass::LightKeyHasher::operator()(const LightKey& lightKey) const
{
std::size_t lightHash = 5;
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
{
return currentHash * 23 + newHash;
};
std::hash<const Light*> lightPtrHasher;
for (std::size_t i = 0; i < lightKey.size(); ++i)
lightHash = CombineHash(lightHash, lightPtrHasher(lightKey[i]));
return lightHash;
}
} }
#include <Nazara/Graphics/DebugOff.hpp> #include <Nazara/Graphics/DebugOff.hpp>

View File

@ -11,6 +11,7 @@
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/LightShadowData.hpp> #include <Nazara/Graphics/LightShadowData.hpp>
#include <Nazara/Math/BoundingVolume.hpp> #include <Nazara/Math/BoundingVolume.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Math/Quaternion.hpp> #include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector3.hpp> #include <Nazara/Math/Vector3.hpp>
#include <Nazara/Utility/PixelFormat.hpp> #include <Nazara/Utility/PixelFormat.hpp>
@ -19,12 +20,8 @@
namespace Nz namespace Nz
{ {
class CommandBufferBuilder;
class ElementRendererRegistry; class ElementRendererRegistry;
class FramePipeline; class FramePipeline;
class RenderBuffer;
class RenderFrame;
class Texture;
class NAZARA_GRAPHICS_API Light class NAZARA_GRAPHICS_API Light
{ {
@ -34,11 +31,11 @@ namespace Nz
Light(Light&&) noexcept = default; Light(Light&&) noexcept = default;
virtual ~Light(); virtual ~Light();
virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0; virtual float ComputeContributionScore(const Frustumf& viewerFrustum) const = 0;
inline void EnableShadowCasting(bool castShadows); inline void EnableShadowCasting(bool castShadows);
virtual void FillLightData(void* data) const = 0; virtual bool FrustumCull(const Frustumf& viewerFrustum) const = 0;
inline const BoundingVolumef& GetBoundingVolume() const; inline const BoundingVolumef& GetBoundingVolume() const;
inline UInt8 GetLightType() const; inline UInt8 GetLightType() const;

View File

@ -12,6 +12,7 @@
namespace Nz namespace Nz
{ {
class AbstractViewer;
class BakedFrameGraph; class BakedFrameGraph;
class FrameGraph; class FrameGraph;
class FramePass; class FramePass;
@ -22,25 +23,33 @@ namespace Nz
class NAZARA_GRAPHICS_API LightShadowData class NAZARA_GRAPHICS_API LightShadowData
{ {
public: public:
LightShadowData() = default; inline LightShadowData();
LightShadowData(const LightShadowData&) = delete; LightShadowData(const LightShadowData&) = delete;
LightShadowData(LightShadowData&&) = delete; LightShadowData(LightShadowData&&) = delete;
virtual ~LightShadowData(); virtual ~LightShadowData();
virtual void PrepareRendering(RenderFrame& renderFrame) = 0; inline bool IsPerViewer() const;
virtual void PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer) = 0;
virtual void RegisterMaterialInstance(const MaterialInstance& matInstance) = 0; virtual void RegisterMaterialInstance(const MaterialInstance& matInstance) = 0;
virtual void RegisterPassInputs(FramePass& pass) = 0; virtual void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) = 0;
virtual void RegisterToFrameGraph(FrameGraph& frameGraph) = 0; virtual void RegisterToFrameGraph(FrameGraph& frameGraph, const AbstractViewer* viewer) = 0;
virtual void RegisterViewer(const AbstractViewer* viewer);
virtual const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const = 0; virtual const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* viewer) const = 0;
virtual void UnregisterMaterialInstance(const MaterialInstance& matInstance) = 0; virtual void UnregisterMaterialInstance(const MaterialInstance& matInstance) = 0;
virtual void UnregisterViewer(const AbstractViewer* viewer);
LightShadowData& operator=(const LightShadowData&) = delete; LightShadowData& operator=(const LightShadowData&) = delete;
LightShadowData& operator=(LightShadowData&&) = delete; LightShadowData& operator=(LightShadowData&&) = delete;
protected:
inline void UpdatePerViewerStatus(bool isPerViewer);
private: private:
bool m_isPerViewer;
}; };
} }

View File

@ -6,6 +6,20 @@
namespace Nz namespace Nz
{ {
inline LightShadowData::LightShadowData() :
m_isPerViewer(false)
{
}
inline void LightShadowData::UpdatePerViewerStatus(bool isPerViewer)
{
m_isPerViewer = isPerViewer;
}
inline bool LightShadowData::IsPerViewer() const
{
return m_isPerViewer;
}
} }
#include <Nazara/Graphics/DebugOff.hpp> #include <Nazara/Graphics/DebugOff.hpp>

View File

@ -23,9 +23,9 @@ namespace Nz
PointLight(PointLight&&) noexcept = default; PointLight(PointLight&&) noexcept = default;
~PointLight() = default; ~PointLight() = default;
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const Frustumf& viewerFrustum) const override;
void FillLightData(void* data) const override; bool FrustumCull(const Frustumf& viewerFrustum) const override;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;
@ -33,6 +33,7 @@ namespace Nz
inline float GetDiffuseFactor() const; inline float GetDiffuseFactor() const;
inline Color GetColor() const; inline Color GetColor() const;
inline const Vector3f& GetPosition() const; inline const Vector3f& GetPosition() const;
inline float GetInvRadius() const;
inline float GetRadius() const; inline float GetRadius() const;
inline void UpdateAmbientFactor(float factor); inline void UpdateAmbientFactor(float factor);

View File

@ -28,14 +28,19 @@ namespace Nz
return m_color; return m_color;
} }
inline float PointLight::GetDiffuseFactor() const
{
return m_diffuseFactor;
}
inline const Vector3f& PointLight::GetPosition() const inline const Vector3f& PointLight::GetPosition() const
{ {
return m_position; return m_position;
} }
inline float PointLight::GetDiffuseFactor() const inline float PointLight::GetInvRadius() const
{ {
return m_diffuseFactor; return m_invRadius;
} }
inline float PointLight::GetRadius() const inline float PointLight::GetRadius() const

View File

@ -28,13 +28,13 @@ namespace Nz
PointLightShadowData(PointLightShadowData&&) = delete; PointLightShadowData(PointLightShadowData&&) = delete;
~PointLightShadowData() = default; ~PointLightShadowData() = default;
void PrepareRendering(RenderFrame& renderFrame) override; void PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer) override;
void RegisterMaterialInstance(const MaterialInstance& matInstance) override; void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
void RegisterPassInputs(FramePass& pass) override; void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) override;
void RegisterToFrameGraph(FrameGraph& frameGraph) override; void RegisterToFrameGraph(FrameGraph& frameGraph, const AbstractViewer* viewer) override;
const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const override; const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* viewer) const override;
void UnregisterMaterialInstance(const MaterialInstance& matInstance) override; void UnregisterMaterialInstance(const MaterialInstance& matInstance) override;

View File

@ -24,19 +24,24 @@ namespace Nz
SpotLight(SpotLight&&) noexcept = default; SpotLight(SpotLight&&) noexcept = default;
~SpotLight() = default; ~SpotLight() = default;
float ComputeContributionScore(const BoundingVolumef& boundingVolume) const override; float ComputeContributionScore(const Frustumf& viewerFrustum) const override;
void FillLightData(void* data) const override; bool FrustumCull(const Frustumf& viewerFrustum) const override;
inline float GetAmbientFactor() const; inline float GetAmbientFactor() const;
inline float GetDiffuseFactor() const; inline float GetDiffuseFactor() const;
inline Color GetColor() const; inline Color GetColor() const;
inline const Vector3f& GetDirection() const; inline const Vector3f& GetDirection() const;
inline RadianAnglef GetInnerAngle() const; inline RadianAnglef GetInnerAngle() const;
inline float GetInnerAngleCos() const;
inline float GetInvRadius() const;
inline RadianAnglef GetOuterAngle() const; inline RadianAnglef GetOuterAngle() const;
inline float GetOuterAngleCos() const;
inline float GetOuterAngleTan() const;
inline const Vector3f& GetPosition() const; inline const Vector3f& GetPosition() const;
inline const Quaternionf& GetRotation() const; inline const Quaternionf& GetRotation() const;
inline float GetRadius() const; inline float GetRadius() const;
inline const Matrix4f& GetViewProjMatrix() const;
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override; std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;

View File

@ -31,6 +31,11 @@ namespace Nz
return m_color; return m_color;
} }
inline float SpotLight::GetDiffuseFactor() const
{
return m_diffuseFactor;
}
inline const Vector3f& SpotLight::GetDirection() const inline const Vector3f& SpotLight::GetDirection() const
{ {
return m_direction; return m_direction;
@ -41,11 +46,31 @@ namespace Nz
return m_innerAngle; return m_innerAngle;
} }
inline float SpotLight::GetInnerAngleCos() const
{
return m_innerAngleCos;
}
inline float SpotLight::GetInvRadius() const
{
return m_invRadius;
}
inline RadianAnglef SpotLight::GetOuterAngle() const inline RadianAnglef SpotLight::GetOuterAngle() const
{ {
return m_outerAngle; return m_outerAngle;
} }
inline float SpotLight::GetOuterAngleCos() const
{
return m_outerAngleCos;
}
inline float SpotLight::GetOuterAngleTan() const
{
return m_outerAngleTan;
}
inline const Vector3f& SpotLight::GetPosition() const inline const Vector3f& SpotLight::GetPosition() const
{ {
return m_position; return m_position;
@ -56,16 +81,16 @@ namespace Nz
return m_rotation; return m_rotation;
} }
inline float SpotLight::GetDiffuseFactor() const
{
return m_diffuseFactor;
}
inline float SpotLight::GetRadius() const inline float SpotLight::GetRadius() const
{ {
return m_radius; return m_radius;
} }
inline const Matrix4f& SpotLight::GetViewProjMatrix() const
{
return m_viewProjMatrix;
}
inline void SpotLight::UpdateAmbientFactor(float factor) inline void SpotLight::UpdateAmbientFactor(float factor)
{ {
m_ambientFactor = factor; m_ambientFactor = factor;
@ -164,10 +189,10 @@ namespace Nz
inline void SpotLight::UpdateViewProjMatrix() inline void SpotLight::UpdateViewProjMatrix()
{ {
Matrix4f biasMatrix(0.5f, 0.0f, 0.0f, 0.0f, constexpr Matrix4f biasMatrix(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f); 0.5f, 0.5f, 0.0f, 1.0f);
Matrix4f projection = Matrix4f::Perspective(m_outerAngle * 2.f, 1.f, 0.01f, m_radius); Matrix4f projection = Matrix4f::Perspective(m_outerAngle * 2.f, 1.f, 0.01f, m_radius);
Matrix4f view = Matrix4f::TransformInverse(m_position, m_rotation); Matrix4f view = Matrix4f::TransformInverse(m_position, m_rotation);

View File

@ -26,13 +26,15 @@ namespace Nz
SpotLightShadowData(SpotLightShadowData&&) = delete; SpotLightShadowData(SpotLightShadowData&&) = delete;
~SpotLightShadowData() = default; ~SpotLightShadowData() = default;
void PrepareRendering(RenderFrame& renderFrame) override; inline const ViewerInstance& GetViewerInstance() const;
void PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer) override;
void RegisterMaterialInstance(const MaterialInstance& matInstance) override; void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
void RegisterPassInputs(FramePass& pass) override; void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) override;
void RegisterToFrameGraph(FrameGraph& frameGraph) override; void RegisterToFrameGraph(FrameGraph& frameGraph, const AbstractViewer* viewer) override;
const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const override; const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* viewer) const override;
void UnregisterMaterialInstance(const MaterialInstance& matInstance) override; void UnregisterMaterialInstance(const MaterialInstance& matInstance) override;

View File

@ -6,6 +6,10 @@
namespace Nz namespace Nz
{ {
inline const ViewerInstance& SpotLightShadowData::GetViewerInstance() const
{
return m_viewer.GetViewerInstance();
}
} }
#include <Nazara/Graphics/DebugOff.hpp> #include <Nazara/Graphics/DebugOff.hpp>

View File

@ -59,7 +59,7 @@ namespace Nz
RenderElementPool<RenderSpriteChain>& GetPool() override; RenderElementPool<RenderSpriteChain>& GetPool() override;
std::unique_ptr<ElementRendererData> InstanciateData() override; std::unique_ptr<ElementRendererData> InstanciateData() override;
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) override; void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, SparsePtr<const RenderStates> renderStates) override;
void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData) override; void PrepareEnd(RenderFrame& currentFrame, ElementRendererData& rendererData) override;
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override; void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override;
void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override; void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;

View File

@ -27,7 +27,7 @@ namespace Nz
RenderElementPool<RenderSubmesh>& GetPool() override; RenderElementPool<RenderSubmesh>& GetPool() override;
std::unique_ptr<ElementRendererData> InstanciateData() override; std::unique_ptr<ElementRendererData> InstanciateData() override;
void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) override; void Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, SparsePtr<const RenderStates> renderStates) override;
void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override; void Render(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, std::size_t elementCount, const Pointer<const RenderElement>* elements) override;
void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override; void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;

View File

@ -56,6 +56,11 @@ namespace Nz
constexpr IntersectionSide Intersect(const Sphere<T>& sphere) const; constexpr IntersectionSide Intersect(const Sphere<T>& sphere) const;
constexpr IntersectionSide Intersect(const Vector3<T>* points, std::size_t pointCount) const; constexpr IntersectionSide Intersect(const Vector3<T>* points, std::size_t pointCount) const;
constexpr Frustum<T> Reduce(T nearFactor, T farFactor) const;
template<typename F> constexpr void Split(std::initializer_list<T> splitFactors, F&& callback) const;
template<typename F> constexpr void Split(const T* splitFactors, std::size_t factorCount, F&& callback) const;
std::string ToString() const; std::string ToString() const;
constexpr Frustum& operator=(const Frustum&) = default; constexpr Frustum& operator=(const Frustum&) = default;

View File

@ -427,6 +427,38 @@ namespace Nz
return side; return side;
} }
template<typename T>
constexpr Frustum<T> Frustum<T>::Reduce(T nearFactor, T farFactor) const
{
EnumArray<FrustumPlane, Plane<T>> planes = m_planes;
planes[FrustumPlane::Near].distance = Lerp(m_planes[FrustumPlane::Near].distance, -m_planes[FrustumPlane::Far].distance, nearFactor);
planes[FrustumPlane::Far].distance = Lerp(-m_planes[FrustumPlane::Near].distance, m_planes[FrustumPlane::Far].distance, farFactor);
return Frustum<T>(planes);
}
template<typename T>
template<typename F>
constexpr void Frustum<T>::Split(std::initializer_list<T> splitFactors, F&& callback) const
{
return Split(splitFactors.begin(), splitFactors.size(), std::forward<F>(callback));
}
template<typename T>
template<typename F>
constexpr void Frustum<T>::Split(const T* splitFactors, std::size_t factorCount, F&& callback) const
{
T previousFar = T(0.0);
for (std::size_t i = 0; i < factorCount; ++i)
{
T farFactor = splitFactors[i];
callback(previousFar, farFactor);
previousFar = farFactor;
}
callback(previousFar, T(1.0));
}
/*! /*!
* \brief Gives a string representation * \brief Gives a string representation
* \return A string representation of the object: "Frustum(Plane ...)" * \return A string representation of the object: "Frustum(Plane ...)"

View File

@ -83,14 +83,13 @@ namespace Nz
const auto& viewerInstance = m_viewer->GetViewerInstance(); const auto& viewerInstance = m_viewer->GetViewerInstance();
ElementRenderer::RenderStates defaultRenderStates{};
m_elementRegistry.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount) m_elementRegistry.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
{ {
ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType); ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType);
m_renderStates.clear(); elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, SparsePtr(&defaultRenderStates, 0));
m_renderStates.resize(elementCount);
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());
}); });
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)

View File

@ -3,36 +3,24 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/DirectionalLight.hpp> #include <Nazara/Graphics/DirectionalLight.hpp>
#include <Nazara/Graphics/Enums.hpp> #include <Nazara/Graphics/DirectionalLightShadowData.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Math/Vector4.hpp>
#include <limits>
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
namespace Nz namespace Nz
{ {
float DirectionalLight::ComputeContributionScore(const BoundingVolumef& /*boundingVolume*/) const float DirectionalLight::ComputeContributionScore(const Frustumf& /*viewerFrustum*/) const
{ {
return -std::numeric_limits<float>::infinity(); return -std::numeric_limits<float>::infinity();
} }
void DirectionalLight::FillLightData(void* data) const bool DirectionalLight::FrustumCull(const Frustumf& /*viewerFrustum*/) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); return true; //< always visible
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Directional);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r, m_color.g, m_color.b, m_color.a);
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f);
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f);
} }
std::unique_ptr<LightShadowData> DirectionalLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const std::unique_ptr<LightShadowData> DirectionalLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
{ {
return nullptr; //< TODO return std::make_unique<DirectionalLightShadowData>(pipeline, elementRegistry, *this, 4);
} }
void DirectionalLight::UpdateTransform(const Vector3f& /*position*/, const Quaternionf& rotation, const Vector3f& /*scale*/) void DirectionalLight::UpdateTransform(const Vector3f& /*position*/, const Quaternionf& rotation, const Vector3f& /*scale*/)

View File

@ -0,0 +1,278 @@
// Copyright (C) 2023 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/DirectionalLightShadowData.hpp>
#include <Nazara/Graphics/BakedFrameGraph.hpp>
#include <Nazara/Graphics/DirectionalLight.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Sphere.hpp>
#include <NazaraUtils/Algorithm.hpp>
#include <NazaraUtils/StackVector.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
DirectionalLightShadowData::DirectionalLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const DirectionalLight& light, std::size_t cascadeCount) :
m_cascadeCount(cascadeCount),
m_elementRegistry(elementRegistry),
m_pipeline(pipeline),
m_light(light),
m_isShadowStabilizationEnabled(true)
{
m_onLightShadowMapSettingChange.Connect(m_light.OnLightShadowMapSettingChange, [this](Light* /*light*/, PixelFormat /*newPixelFormat*/, UInt32 newSize)
{
m_texelScale = 2.0f / newSize;
m_invTexelScale = 1.0f / m_texelScale;
ForEachCascade([newSize](CascadeData& cascade)
{
cascade.viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(newSize), SafeCast<int>(newSize)));
});
});
m_texelScale = 2.0f / m_light.GetShadowMapSize();
m_invTexelScale = 1.0f / m_texelScale;
UpdatePerViewerStatus(true);
}
void DirectionalLightShadowData::PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer)
{
assert(viewer);
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
// Extract frustum from main viewer
const Vector3f& eyePosition = viewer->GetViewerInstance().GetEyePosition();
const Matrix4f& viewProjMatrix = viewer->GetViewerInstance().GetViewProjMatrix();
//std::array planePct = { 0.1f, 0.3f, 0.6f }; // TODO: Generate the separations based on other settings
std::array planePct = { 0.03f, 0.1f, 0.2f, 0.5f }; // TODO: Generate the separations based on other settings
assert(m_cascadeCount <= planePct.size() + 1);
StackVector<Frustumf> frustums = NazaraStackVector(Frustumf, m_cascadeCount);
StackVector<float> frustumDists = NazaraStackVector(float, m_cascadeCount);
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
frustum.Split(planePct.data(), m_cascadeCount - 1, [&](float zNearPct, float zFarPct)
{
frustums.push_back(frustum.Reduce(zNearPct, zFarPct));
frustumDists.push_back(frustums.back().GetPlane(FrustumPlane::Far).SignedDistance(eyePosition));
});
for (std::size_t cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
{
CascadeData& cascade = viewerData.cascades[cascadeIndex];
ComputeLightView(cascade, frustums[cascadeIndex], frustumDists[cascadeIndex]);
if (m_isShadowStabilizationEnabled)
StabilizeShadows(cascade);
constexpr Matrix4f biasMatrix(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f);
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
cascade.viewProjMatrix = viewerInstance.GetViewProjMatrix() * biasMatrix;
m_pipeline.QueueTransfer(&viewerInstance);
// Prepare depth pass
Frustumf lightFrustum = Frustumf::Extract(viewerInstance.GetViewProjMatrix());
std::size_t visibilityHash = 5U;
const auto& visibleRenderables = m_pipeline.FrustumCull(lightFrustum, 0xFFFFFFFF, visibilityHash);
cascade.depthPass->Prepare(renderFrame, lightFrustum, visibleRenderables, visibilityHash);
}
}
void DirectionalLightShadowData::ComputeLightView(CascadeData& cascade, const Frustumf& cascadeFrustum, float cascadeDist)
{
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
EnumArray<BoxCorner, Vector3f> frustumCorners = cascadeFrustum.ComputeCorners();
// Get frustum center
Vector3f frustumCenter = Vector3f::Zero();
for (const Vector3f& corner : frustumCorners)
frustumCenter += corner;
frustumCenter /= float(frustumCorners.size());
// Compute radius (= biggest distance to the center)
float radius = 0.f;
for (const Vector3f& corner : frustumCorners)
radius = std::max(radius, frustumCenter.SquaredDistance(corner));
radius = std::ceil(std::sqrt(radius));
Matrix4f lightView = Matrix4f::TransformInverse(frustumCenter, m_light.GetRotation());
// Compute light projection matrix
Boxf aabb = Boxf::FromExtends(frustumCenter - Vector3f(radius), frustumCenter + Vector3f(radius));
float left = std::numeric_limits<float>::infinity();
float right = -std::numeric_limits<float>::infinity();
float top = std::numeric_limits<float>::infinity();
float bottom = -std::numeric_limits<float>::infinity();
float zNear = std::numeric_limits<float>::infinity();
float zFar = -std::numeric_limits<float>::infinity();
for (const Vector3f& corner : aabb.GetCorners())
{
Vector3f viewCorner = lightView * corner;
left = std::min(left, viewCorner.x);
right = std::max(right, viewCorner.x);
top = std::min(top, viewCorner.y);
bottom = std::max(bottom, viewCorner.y);
zNear = std::min(zNear, viewCorner.z);
zFar = std::max(zFar, viewCorner.z);
}
// Tune this parameter according to the scene
constexpr float zMult = 2.0f;
if (zNear < 0)
zNear *= zMult;
else
zNear /= zMult;
if (zFar < 0)
zFar /= zMult;
else
zFar *= zMult;
cascade.distance = cascadeDist;
Matrix4f lightProj = Matrix4f::Ortho(left, right, top, bottom, zNear, zFar);
viewerInstance.UpdateProjViewMatrices(lightProj, lightView);
viewerInstance.UpdateEyePosition(frustumCenter);
viewerInstance.UpdateNearFarPlanes(zNear, zFar);
}
void DirectionalLightShadowData::StabilizeShadows(CascadeData& cascade)
{
ViewerInstance& viewerInstance = cascade.viewer.GetViewerInstance();
// Stabilize cascade shadows by keeping the center to a texel boundary of the previous frame
// https://www.junkship.net/News/2020/11/22/shadow-of-a-doubt-part-2
Vector4f shadowOrigin(0.0f, 0.0f, 0.0f, 1.0f);
shadowOrigin = viewerInstance.GetViewProjMatrix() * shadowOrigin;
shadowOrigin *= m_texelScale;
Vector2f roundedOrigin = { std::round(shadowOrigin.x), std::round(shadowOrigin.y) };
Vector2f roundOffset = roundedOrigin - Vector2f(shadowOrigin);
roundOffset *= m_invTexelScale;
Matrix4f lightProj = viewerInstance.GetProjectionMatrix();
lightProj.ApplyTranslation(Vector3f(roundOffset.x, roundOffset.y, 0.f));
viewerInstance.UpdateProjectionMatrix(lightProj);
}
void DirectionalLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance)
{
ForEachCascade([&](CascadeData& cascade)
{
cascade.depthPass->RegisterMaterialInstance(matInstance);
});
}
void DirectionalLightShadowData::RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer)
{
assert(viewer);
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
std::size_t arrayInputIndex = pass.AddInput(viewerData.textureArrayAttachmentIndex);
pass.SetInputLayout(arrayInputIndex, TextureLayout::ColorInput);
for (CascadeData& cascade : viewerData.cascades)
pass.AddInput(cascade.attachmentIndex);
}
void DirectionalLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph, const AbstractViewer* viewer)
{
UInt32 shadowMapSize = m_light.GetShadowMapSize();
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
viewerData.textureArrayAttachmentIndex = frameGraph.AddAttachmentArray({
"Directional-light cascade shadowmaps",
m_light.GetShadowMapFormat(),
FramePassAttachmentSize::Fixed,
shadowMapSize, shadowMapSize,
}, m_cascadeCount);
for (std::size_t i = 0; i < viewerData.cascades.size(); ++i)
{
CascadeData& cascade = viewerData.cascades[i];
cascade.attachmentIndex = frameGraph.AddAttachmentArrayLayer(viewerData.textureArrayAttachmentIndex, i);
cascade.depthPass->RegisterToFrameGraph(frameGraph, cascade.attachmentIndex);
}
}
void DirectionalLightShadowData::RegisterViewer(const AbstractViewer* viewer)
{
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass");
std::unique_ptr<PerViewerData> perViewerData = std::make_unique<PerViewerData>();
perViewerData->cascades.resize(m_cascadeCount);
std::size_t cascadeIndex = 0;
UInt32 shadowMapSize = m_light.GetShadowMapSize();
for (CascadeData& cascade : perViewerData->cascades)
{
ShadowViewer& shadowViewer = cascade.viewer;
shadowViewer.UpdateRenderMask(0xFFFFFFFF);
shadowViewer.UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
cascade.depthPass.emplace(m_pipeline, m_elementRegistry, &shadowViewer, shadowPassIndex, Format("Cascade #{}", cascadeIndex++));
}
m_pipeline.ForEachRegisteredMaterialInstance([&](const MaterialInstance& matInstance)
{
for (CascadeData& cascade : perViewerData->cascades)
cascade.depthPass->RegisterMaterialInstance(matInstance);
});
assert(m_viewerData.find(viewer) == m_viewerData.end());
m_viewerData[viewer] = std::move(perViewerData);
}
const Texture* DirectionalLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* viewer) const
{
assert(viewer);
const PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
return bakedGraph.GetAttachmentTexture(viewerData.textureArrayAttachmentIndex).get();
}
void DirectionalLightShadowData::UnregisterMaterialInstance(const MaterialInstance& matInstance)
{
ForEachCascade([&](CascadeData& cascade)
{
cascade.depthPass->UnregisterMaterialInstance(matInstance);
});
}
void DirectionalLightShadowData::UnregisterViewer(const AbstractViewer* viewer)
{
m_viewerData.erase(viewer);
}
template<typename F>
void DirectionalLightShadowData::ForEachCascade(F&& callback)
{
for (auto it = m_viewerData.begin(); it != m_viewerData.end(); ++it)
{
for (CascadeData& cascade : it->second->cascades)
callback(cascade);
}
}
}

View File

@ -9,7 +9,7 @@ namespace Nz
{ {
ElementRenderer::~ElementRenderer() = default; ElementRenderer::~ElementRenderer() = default;
void ElementRenderer::Prepare(const ViewerInstance& /*viewerInstance*/, ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/, std::size_t /*elementCount*/, const Pointer<const RenderElement>* /*elements*/, const RenderStates* /*renderStates*/) void ElementRenderer::Prepare(const ViewerInstance& /*viewerInstance*/, ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/, std::size_t /*elementCount*/, const Pointer<const RenderElement>* /*elements*/, SparsePtr<const RenderStates> /*renderStates*/)
{ {
} }

View File

@ -112,6 +112,9 @@ namespace Nz
//TODO: Switch lights to storage buffers so they can all be part of GPU memory //TODO: Switch lights to storage buffers so they can all be part of GPU memory
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderMask) if (viewerRenderMask & renderMask)
@ -125,6 +128,17 @@ namespace Nz
{ {
m_shadowCastingLights.UnboundedSet(lightIndex); m_shadowCastingLights.UnboundedSet(lightIndex);
lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry); lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry);
if (lightData->shadowData->IsPerViewer())
{
for (auto& viewerData : m_viewerPool)
{
if (viewerData.pendingDestruction)
continue;
if ((viewerData.viewer->GetRenderMask() & lightData->renderMask) != 0)
lightData->shadowData->RegisterViewer(viewerData.viewer);
}
}
} }
else else
{ {
@ -139,6 +153,18 @@ namespace Nz
{ {
m_shadowCastingLights.UnboundedSet(lightIndex); m_shadowCastingLights.UnboundedSet(lightIndex);
lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry); lightData->shadowData = light->InstanciateShadowData(*this, m_elementRegistry);
if (lightData->shadowData->IsPerViewer())
{
for (auto& viewerData : m_viewerPool)
{
if (viewerData.pendingDestruction)
continue;
if ((viewerData.viewer->GetRenderMask() & lightData->renderMask) != 0)
lightData->shadowData->RegisterViewer(viewerData.viewer);
}
}
m_rebuildFrameGraph = true; m_rebuildFrameGraph = true;
} }
@ -161,6 +187,9 @@ namespace Nz
// TODO: Invalidate only relevant viewers and passes // TODO: Invalidate only relevant viewers and passes
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderMask) if (viewerRenderMask & renderMask)
@ -181,6 +210,9 @@ namespace Nz
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
if (viewerData.depthPrepass) if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterialInstance(*newMaterial); viewerData.depthPrepass->RegisterMaterialInstance(*newMaterial);
@ -195,6 +227,9 @@ namespace Nz
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
if (viewerData.depthPrepass) if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterialInstance(*prevMaterial); viewerData.depthPrepass->UnregisterMaterialInstance(*prevMaterial);
@ -212,6 +247,9 @@ namespace Nz
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
if (viewerData.depthPrepass) if (viewerData.depthPrepass)
viewerData.depthPrepass->RegisterMaterialInstance(*mat); viewerData.depthPrepass->RegisterMaterialInstance(*mat);
@ -262,6 +300,14 @@ namespace Nz
m_transferSet.insert(&viewerInstance->GetViewerInstance()); m_transferSet.insert(&viewerInstance->GetViewerInstance());
UInt32 renderMask = viewerInstance->GetRenderMask();
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
lightData->shadowData->RegisterViewer(viewerInstance);
}
m_rebuildFrameGraph = true; m_rebuildFrameGraph = true;
return viewerIndex; return viewerIndex;
@ -287,12 +333,21 @@ namespace Nz
return m_lightPool.RetrieveFromIndex(lightIndex)->light; return m_lightPool.RetrieveFromIndex(lightIndex)->light;
} }
const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex) const const LightShadowData* ForwardFramePipeline::RetrieveLightShadowData(std::size_t lightIndex) const
{ {
if (!m_shadowCastingLights.UnboundedTest(lightIndex)) if (!m_shadowCastingLights.UnboundedTest(lightIndex))
return nullptr; return nullptr;
return m_lightPool.RetrieveFromIndex(lightIndex)->shadowData->RetrieveLightShadowmap(m_bakedFrameGraph); return m_lightPool.RetrieveFromIndex(lightIndex)->shadowData.get();
}
const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex, const AbstractViewer* viewer) const
{
const LightShadowData* lightShadowData = RetrieveLightShadowData(lightIndex);
if (!lightShadowData)
return nullptr;
return lightShadowData->RetrieveLightShadowmap(m_bakedFrameGraph, viewer);
} }
void ForwardFramePipeline::Render(RenderFrame& renderFrame) void ForwardFramePipeline::Render(RenderFrame& renderFrame)
@ -334,75 +389,67 @@ namespace Nz
else else
frameGraphInvalidated = m_bakedFrameGraph.Resize(renderFrame); frameGraphInvalidated = m_bakedFrameGraph.Resize(renderFrame);
// Update UBOs and materials // Find active lights (i.e. visible in any frustum)
renderFrame.Execute([&](CommandBufferBuilder& builder) m_activeLights.Clear();
{
builder.BeginDebugRegion("CPU to GPU transfers", Color::Yellow());
{
builder.PreTransferBarrier();
for (TransferInterface* transferInterface : m_transferSet)
transferInterface->OnTransfer(renderFrame, builder);
m_transferSet.clear();
OnTransfer(this, renderFrame, builder);
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
// 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);
lightData->shadowData->PrepareRendering(renderFrame);
}
// Render queues handling
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 renderMask = viewerData.viewer->GetRenderMask(); UInt32 renderMask = viewerData.viewer->GetRenderMask();
// Frustum culling // Extract frustum from viewproj matrix
const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix(); const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix();
viewerData.frame.frustum = Frustumf::Extract(viewProjMatrix);
Frustumf frustum = Frustumf::Extract(viewProjMatrix); viewerData.frame.visibleLights.Clear();
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;
m_visibleLights.clear();
for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it) for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it)
{ {
const LightData& lightData = *it; const LightData& lightData = *it;
std::size_t lightIndex = it.GetIndex(); std::size_t lightIndex = it.GetIndex();
const BoundingVolumef& boundingVolume = lightData.light->GetBoundingVolume(); if ((lightData.renderMask & renderMask) == 0)
continue;
// TODO: Use more precise tests for point lights (frustum/sphere is cheap) m_activeLights.UnboundedSet(lightIndex);
if (renderMask & lightData.renderMask && frustum.Intersect(boundingVolume) != IntersectionSide::Outside) viewerData.frame.visibleLights.UnboundedSet(lightIndex);
{ }
m_visibleLights.push_back(lightIndex); }
auto CombineHash = [](std::size_t currentHash, std::size_t newHash) m_visibleShadowCastingLights.PerformsAND(m_activeLights, m_shadowCastingLights);
{
return currentHash * 23 + newHash;
};
visibilityHash = CombineHash(visibilityHash, std::hash<const void*>()(lightData.light)); // Shadow map handling (for active lights)
} for (std::size_t i = m_visibleShadowCastingLights.FindFirst(); i != m_visibleShadowCastingLights.npos; i = m_visibleShadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
if (!lightData->shadowData->IsPerViewer())
lightData->shadowData->PrepareRendering(renderFrame, nullptr);
}
// Viewer handling (second pass)
for (auto& viewerData : m_viewerPool)
{
if (viewerData.pendingDestruction)
continue;
UInt32 renderMask = viewerData.viewer->GetRenderMask();
// Per-viewer shadow map handling
for (std::size_t lightIndex = viewerData.frame.visibleLights.FindFirst(); lightIndex != viewerData.frame.visibleLights.npos; lightIndex = viewerData.frame.visibleLights.FindNext(lightIndex))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(lightIndex);
if (lightData->shadowData && lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
lightData->shadowData->PrepareRendering(renderFrame, viewerData.viewer);
} }
if (viewerData.depthPrepass) // Frustum culling
viewerData.depthPrepass->Prepare(renderFrame, frustum, visibleRenderables, depthVisibilityHash); std::size_t visibilityHash = 5;
const auto& visibleRenderables = FrustumCull(viewerData.frame.frustum, renderMask, visibilityHash);
if (viewerData.gammaCorrectionPass) if (viewerData.gammaCorrectionPass)
viewerData.gammaCorrectionPass->Prepare(renderFrame); viewerData.gammaCorrectionPass->Prepare(renderFrame);
viewerData.forwardPass->Prepare(renderFrame, frustum, visibleRenderables, m_visibleLights, visibilityHash); viewerData.forwardPass->Prepare(renderFrame, viewerData.frame.frustum, visibleRenderables, viewerData.frame.visibleLights, visibilityHash);
if (viewerData.debugDrawPass) if (viewerData.debugDrawPass)
viewerData.debugDrawPass->Prepare(renderFrame); viewerData.debugDrawPass->Prepare(renderFrame);
@ -413,6 +460,9 @@ namespace Nz
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({}); const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
if (viewerData.blitShaderBinding) if (viewerData.blitShaderBinding)
renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding)); renderFrame.PushForRelease(std::move(viewerData.blitShaderBinding));
@ -446,6 +496,24 @@ namespace Nz
} }
} }
// Update UBOs and materials
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.BeginDebugRegion("CPU to GPU transfers", Color::Yellow());
{
builder.PreTransferBarrier();
for (TransferInterface* transferInterface : m_transferSet)
transferInterface->OnTransfer(renderFrame, builder);
m_transferSet.clear();
OnTransfer(this, renderFrame, builder);
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
m_bakedFrameGraph.Execute(renderFrame); m_bakedFrameGraph.Execute(renderFrame);
m_rebuildFrameGraph = false; m_rebuildFrameGraph = false;
@ -494,7 +562,11 @@ namespace Nz
void ForwardFramePipeline::UnregisterLight(std::size_t lightIndex) void ForwardFramePipeline::UnregisterLight(std::size_t lightIndex)
{ {
m_lightPool.Free(lightIndex); m_lightPool.Free(lightIndex);
m_shadowCastingLights.UnboundedReset(lightIndex); if (m_shadowCastingLights.UnboundedTest(lightIndex))
{
m_shadowCastingLights.Reset(lightIndex);
m_rebuildFrameGraph = true;
}
} }
void ForwardFramePipeline::UnregisterRenderable(std::size_t renderableIndex) void ForwardFramePipeline::UnregisterRenderable(std::size_t renderableIndex)
@ -509,6 +581,9 @@ namespace Nz
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
if (viewerData.depthPrepass) if (viewerData.depthPrepass)
viewerData.depthPrepass->UnregisterMaterialInstance(*material); viewerData.depthPrepass->UnregisterMaterialInstance(*material);
@ -521,20 +596,31 @@ namespace Nz
void ForwardFramePipeline::UnregisterSkeleton(std::size_t skeletonIndex) void ForwardFramePipeline::UnregisterSkeleton(std::size_t skeletonIndex)
{ {
// Defer world instance release // Defer instance release
m_removedSkeletonInstances.UnboundedSet(skeletonIndex); m_removedSkeletonInstances.UnboundedSet(skeletonIndex);
} }
void ForwardFramePipeline::UnregisterViewer(std::size_t viewerIndex) void ForwardFramePipeline::UnregisterViewer(std::size_t viewerIndex)
{ {
// Defer world instance release auto& viewerData = *m_viewerPool.RetrieveFromIndex(viewerIndex);
viewerData.pendingDestruction = true;
UInt32 renderMask = viewerData.viewer->GetRenderMask();
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
lightData->shadowData->UnregisterViewer(viewerData.viewer);
}
// Defer instance release
m_removedViewerInstances.UnboundedSet(viewerIndex); m_removedViewerInstances.UnboundedSet(viewerIndex);
m_rebuildFrameGraph = true; m_rebuildFrameGraph = true;
} }
void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance) void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance)
{ {
// Defer world instance release // Defer instance release
m_removedWorldInstances.UnboundedSet(worldInstance); m_removedWorldInstances.UnboundedSet(worldInstance);
} }
@ -558,6 +644,9 @@ namespace Nz
// TODO: Invalidate only relevant viewers and passes // TODO: Invalidate only relevant viewers and passes
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderableData->renderMask) if (viewerRenderMask & renderableData->renderMask)
@ -578,6 +667,9 @@ namespace Nz
// TODO: Invalidate only relevant viewers and passes // TODO: Invalidate only relevant viewers and passes
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask(); UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
if (viewerRenderMask & renderableData->renderMask) if (viewerRenderMask & renderableData->renderMask)
@ -593,6 +685,7 @@ namespace Nz
void ForwardFramePipeline::UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder) void ForwardFramePipeline::UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder)
{ {
ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex); ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex);
assert(!viewerData->pendingDestruction);
if (viewerData->renderOrder != renderOrder) if (viewerData->renderOrder != renderOrder)
{ {
viewerData->renderOrder = renderOrder; viewerData->renderOrder = renderOrder;
@ -607,11 +700,23 @@ namespace Nz
for (std::size_t i : m_shadowCastingLights.IterBits()) for (std::size_t i : m_shadowCastingLights.IterBits())
{ {
LightData* lightData = m_lightPool.RetrieveFromIndex(i); LightData* lightData = m_lightPool.RetrieveFromIndex(i);
lightData->shadowData->RegisterToFrameGraph(frameGraph); if (!lightData->shadowData->IsPerViewer())
lightData->shadowData->RegisterToFrameGraph(frameGraph, nullptr);
} }
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
UInt32 renderMask = viewerData.viewer->GetRenderMask();
for (std::size_t i = m_shadowCastingLights.FindFirst(); i != m_shadowCastingLights.npos; i = m_shadowCastingLights.FindNext(i))
{
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
lightData->shadowData->RegisterToFrameGraph(frameGraph, viewerData.viewer);
}
viewerData.forwardColorAttachment = frameGraph.AddAttachment({ viewerData.forwardColorAttachment = frameGraph.AddAttachment({
"Forward output", "Forward output",
PixelFormat::RGBA8 PixelFormat::RGBA8
@ -629,7 +734,8 @@ namespace Nz
for (std::size_t i : m_shadowCastingLights.IterBits()) for (std::size_t i : m_shadowCastingLights.IterBits())
{ {
LightData* lightData = m_lightPool.RetrieveFromIndex(i); LightData* lightData = m_lightPool.RetrieveFromIndex(i);
lightData->shadowData->RegisterPassInputs(forwardPass); if ((renderMask & lightData->renderMask) != 0)
lightData->shadowData->RegisterPassInputs(forwardPass, (lightData->shadowData->IsPerViewer()) ? viewerData.viewer : nullptr);
} }
viewerData.finalColorAttachment = viewerData.forwardColorAttachment; viewerData.finalColorAttachment = viewerData.forwardColorAttachment;
@ -660,6 +766,9 @@ namespace Nz
for (auto& viewerData : m_viewerPool) for (auto& viewerData : m_viewerPool)
{ {
if (viewerData.pendingDestruction)
continue;
const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget(); const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget();
*viewerIt++ = std::make_pair(&renderTarget, &viewerData); *viewerIt++ = std::make_pair(&renderTarget, &viewerData);
} }

View File

@ -4,13 +4,18 @@
#include <Nazara/Graphics/ForwardPipelinePass.hpp> #include <Nazara/Graphics/ForwardPipelinePass.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp> #include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/DirectionalLight.hpp>
#include <Nazara/Graphics/ElementRendererRegistry.hpp> #include <Nazara/Graphics/ElementRendererRegistry.hpp>
#include <Nazara/Graphics/FrameGraph.hpp> #include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/FramePipeline.hpp> #include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/Graphics.hpp> #include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp> #include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Graphics/Material.hpp> #include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/PointLight.hpp>
#include <Nazara/Graphics/SpotLight.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp> #include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Graphics/DirectionalLightShadowData.hpp>
#include <Nazara/Graphics/SpotLightShadowData.hpp>
#include <Nazara/Graphics/ViewerInstance.hpp> #include <Nazara/Graphics/ViewerInstance.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp> #include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/RenderFrame.hpp> #include <Nazara/Renderer/RenderFrame.hpp>
@ -23,155 +28,41 @@ namespace Nz
m_viewer(viewer), m_viewer(viewer),
m_elementRegistry(elementRegistry), m_elementRegistry(elementRegistry),
m_pipeline(owner), m_pipeline(owner),
m_pendingLightUploadAllocation(nullptr),
m_rebuildCommandBuffer(false), m_rebuildCommandBuffer(false),
m_rebuildElements(false) m_rebuildElements(false)
{ {
Graphics* graphics = Graphics::Instance(); Graphics* graphics = Graphics::Instance();
m_forwardPassIndex = graphics->GetMaterialPassRegistry().GetPassIndex("ForwardPass"); m_forwardPassIndex = graphics->GetMaterialPassRegistry().GetPassIndex("ForwardPass");
m_lightUboPool = std::make_shared<LightUboPool>();
std::size_t lightUboAlignedSize = AlignPow2(PredefinedLightOffsets.totalSize, SafeCast<std::size_t>(graphics->GetRenderDevice()->GetDeviceInfo().limits.minUniformBufferOffsetAlignment));
m_lightDataBuffer = graphics->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, lightUboAlignedSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
m_lightDataBuffer->UpdateDebugName("Lights buffer");
m_renderState.lightData = RenderBufferView(m_lightDataBuffer.get());
} }
void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const std::vector<std::size_t>& visibleLights, std::size_t visibilityHash) void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector<FramePipelinePass::VisibleRenderable>& visibleRenderables, const Bitset<UInt64>& visibleLights, std::size_t visibilityHash)
{ {
if (m_lastVisibilityHash != visibilityHash || m_rebuildElements) //< FIXME if (m_lastVisibilityHash != visibilityHash || m_rebuildElements) //< FIXME
{ {
renderFrame.PushForRelease(std::move(m_renderElements)); renderFrame.PushForRelease(std::move(m_renderElements));
m_renderElements.clear(); m_renderElements.clear();
m_renderQueueRegistry.Clear();
m_renderQueue.Clear();
m_lightBufferPerLights.clear();
m_lightPerRenderElement.clear();
for (auto& lightDataUbo : m_lightDataBuffers)
{
renderFrame.PushReleaseCallback([pool = m_lightUboPool, lightUbo = std::move(lightDataUbo.renderBuffer)]() mutable
{
pool->lightUboBuffers.push_back(std::move(lightUbo));
});
}
m_lightDataBuffers.clear();
Graphics* graphics = Graphics::Instance();
PredefinedLightData lightOffsets = PredefinedLightData::GetOffsets();
std::size_t lightUboAlignedSize = AlignPow2(lightOffsets.totalSize, SafeCast<std::size_t>(graphics->GetRenderDevice()->GetDeviceInfo().limits.minUniformBufferOffsetAlignment));
UploadPool& uploadPool = renderFrame.GetUploadPool();
for (const auto& renderableData : visibleRenderables) for (const auto& renderableData : visibleRenderables)
{ {
BoundingVolumef renderableBoundingVolume(renderableData.instancedRenderable->GetAABB());
renderableBoundingVolume.Update(renderableData.worldInstance->GetWorldMatrix());
// Select lights
m_renderableLights.clear();
for (std::size_t lightIndex : visibleLights)
{
const Light* light = m_pipeline.RetrieveLight(lightIndex);
const BoundingVolumef& boundingVolume = light->GetBoundingVolume();
if (boundingVolume.Intersect(renderableBoundingVolume.aabb))
{
float contributionScore = light->ComputeContributionScore(renderableBoundingVolume);
m_renderableLights.push_back({ light, lightIndex, contributionScore });
}
}
// Sort lights
std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const RenderableLight& lhs, const RenderableLight& rhs)
{
return lhs.contributionScore < rhs.contributionScore;
});
std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw);
LightKey lightKey;
lightKey.fill(nullptr);
for (std::size_t i = 0; i < lightCount; ++i)
lightKey[i] = m_renderableLights[i].light;
RenderBufferView lightUboView;
auto it = m_lightBufferPerLights.find(lightKey);
if (it == m_lightBufferPerLights.end())
{
// Prepare light ubo upload
// Find light ubo
LightDataUbo* targetLightData = nullptr;
for (auto& lightUboData : m_lightDataBuffers)
{
if (lightUboData.offset + lightUboAlignedSize <= lightUboData.renderBuffer->GetSize())
{
targetLightData = &lightUboData;
break;
}
}
if (!targetLightData)
{
// Make a new light UBO
auto& lightUboData = m_lightDataBuffers.emplace_back();
// Reuse from pool if possible
if (!m_lightUboPool->lightUboBuffers.empty())
{
lightUboData.renderBuffer = m_lightUboPool->lightUboBuffers.back();
m_lightUboPool->lightUboBuffers.pop_back();
}
else
lightUboData.renderBuffer = graphics->GetRenderDevice()->InstantiateBuffer(BufferType::Uniform, 256 * lightUboAlignedSize, BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
targetLightData = &lightUboData;
}
assert(targetLightData);
if (!targetLightData->allocation)
targetLightData->allocation = &uploadPool.Allocate(targetLightData->renderBuffer->GetSize());
void* lightDataPtr = static_cast<UInt8*>(targetLightData->allocation->mappedPtr) + targetLightData->offset;
AccessByOffset<UInt32&>(lightDataPtr, lightOffsets.lightCountOffset) = SafeCast<UInt32>(lightCount);
UInt8* lightPtr = static_cast<UInt8*>(lightDataPtr) + lightOffsets.lightsOffset;
for (std::size_t i = 0; i < lightCount; ++i)
{
m_renderableLights[i].light->FillLightData(lightPtr);
lightPtr += lightOffsets.lightSize;
}
// Associate render element with light ubo
lightUboView = RenderBufferView(targetLightData->renderBuffer.get(), targetLightData->offset, lightUboAlignedSize);
targetLightData->offset += lightUboAlignedSize;
m_lightBufferPerLights.emplace(lightKey, lightUboView);
}
else
lightUboView = it->second;
InstancedRenderable::ElementData elementData{ InstancedRenderable::ElementData elementData{
&renderableData.scissorBox, &renderableData.scissorBox,
renderableData.skeletonInstance, renderableData.skeletonInstance,
renderableData.worldInstance renderableData.worldInstance
}; };
std::size_t previousCount = m_renderElements.size();
renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_forwardPassIndex, m_renderElements); renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_forwardPassIndex, m_renderElements);
for (std::size_t i = previousCount; i < m_renderElements.size(); ++i)
{
const RenderElement* element = m_renderElements[i].GetElement();
LightPerElementData perElementData;
perElementData.lightCount = lightCount;
perElementData.lightUniformBuffer = lightUboView;
for (std::size_t j = 0; j < lightCount; ++j)
perElementData.shadowMaps[j] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[j].lightIndex);
m_lightPerRenderElement.emplace(element, perElementData);
}
} }
m_renderQueueRegistry.Clear();
m_renderQueue.Clear();
for (const auto& renderElement : m_renderElements) for (const auto& renderElement : m_renderElements)
{ {
renderElement->Register(m_renderQueueRegistry); renderElement->Register(m_renderQueueRegistry);
@ -180,25 +71,8 @@ namespace Nz
m_renderQueueRegistry.Finalize(); m_renderQueueRegistry.Finalize();
renderFrame.Execute([&](CommandBufferBuilder& builder)
{
builder.BeginDebugRegion("Light UBO Update", Color::Yellow());
{
for (auto& lightUboData : m_lightDataBuffers)
{
if (!lightUboData.allocation)
continue;
builder.CopyBuffer(*lightUboData.allocation, RenderBufferView(lightUboData.renderBuffer.get(), 0, lightUboData.offset));
}
builder.PostTransferBarrier();
}
builder.EndDebugRegion();
}, QueueType::Transfer);
m_lastVisibilityHash = visibilityHash; m_lastVisibilityHash = visibilityHash;
m_rebuildElements = true; InvalidateElements();
} }
// TODO: Don't sort every frame if no material pass requires distance sorting // TODO: Don't sort every frame if no material pass requires distance sorting
@ -207,6 +81,8 @@ namespace Nz
return element->ComputeSortingScore(frustum, m_renderQueueRegistry); return element->ComputeSortingScore(frustum, m_renderQueueRegistry);
}); });
PrepareLights(renderFrame, frustum, visibleLights);
if (m_rebuildElements) if (m_rebuildElements)
{ {
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)
@ -222,41 +98,10 @@ namespace Nz
const auto& viewerInstance = m_viewer->GetViewerInstance(); const auto& viewerInstance = m_viewer->GetViewerInstance();
auto& lightPerRenderElement = m_lightPerRenderElement;
m_elementRegistry.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount) m_elementRegistry.ProcessRenderQueue(m_renderQueue, [&](std::size_t elementType, const Pointer<const RenderElement>* elements, std::size_t elementCount)
{ {
ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType); ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType);
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, SparsePtr(&m_renderState, 0));
m_renderStates.clear();
m_renderStates.reserve(elementCount);
for (std::size_t i = 0; i < elementCount; ++i)
{
auto it = lightPerRenderElement.find(elements[i]);
assert(it != lightPerRenderElement.end());
const LightPerElementData& lightData = it->second;
auto& renderStates = m_renderStates.emplace_back();
renderStates.lightData = lightData.lightUniformBuffer;
for (std::size_t j = 0; j < lightData.lightCount; ++j)
{
const Texture* texture = lightData.shadowMaps[j];
if (!texture)
continue;
if (texture->GetType() == ImageType::E2D)
renderStates.shadowMaps2D[j] = texture;
else
{
assert(texture->GetType() == ImageType::Cubemap);
renderStates.shadowMapsCube[j] = texture;
}
}
}
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());
}); });
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer) m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)
@ -342,4 +187,165 @@ namespace Nz
m_materialInstances.erase(it); m_materialInstances.erase(it);
} }
} }
void ForwardPipelinePass::OnTransfer(RenderFrame& renderFrame, CommandBufferBuilder& builder)
{
assert(m_pendingLightUploadAllocation);
builder.CopyBuffer(*m_pendingLightUploadAllocation, RenderBufferView(m_lightDataBuffer.get()));
m_pendingLightUploadAllocation = nullptr;
}
void ForwardPipelinePass::PrepareDirectionalLights(void* lightMemory)
{
std::size_t lightCount = std::min(m_directionalLights.size(), PredefinedLightData::MaxLightCount);
AccessByOffset<UInt32&>(lightMemory, PredefinedLightOffsets.directionalLightCountOffset) = SafeCast<UInt32>(lightCount);
for (std::size_t i = 0; i < lightCount; ++i)
{
UInt8* basePtr = static_cast<UInt8*>(lightMemory) + PredefinedLightOffsets.directionalLightsOffset + PredefinedDirectionalLightOffsets.totalSize * i;
const DirectionalLight* light = m_directionalLights[i].light;
const Color& lightColor = light->GetColor();
AccessByOffset<Vector3f&>(basePtr, PredefinedDirectionalLightOffsets.colorOffset) = Vector3f(lightColor.r, lightColor.g, lightColor.b);
AccessByOffset<Vector3f&>(basePtr, PredefinedDirectionalLightOffsets.directionOffset) = light->GetDirection();
AccessByOffset<float&>(basePtr, PredefinedDirectionalLightOffsets.ambientFactorOffset) = light->GetAmbientFactor();
AccessByOffset<float&>(basePtr, PredefinedDirectionalLightOffsets.diffuseFactorOffset) = light->GetDiffuseFactor();
AccessByOffset<Vector2f&>(basePtr, PredefinedDirectionalLightOffsets.invShadowMapSizeOffset) = (light->IsShadowCaster()) ? Vector2f(1.f / light->GetShadowMapSize()) : Vector2f(-1.f, -1.f);
// Shadowmap handling
const Texture* shadowmap = m_pipeline.RetrieveLightShadowmap(m_directionalLights[i].lightIndex, m_viewer);
if (shadowmap)
{
const DirectionalLightShadowData* shadowData = SafeCast<const DirectionalLightShadowData*>(m_pipeline.RetrieveLightShadowData(m_directionalLights[i].lightIndex));
float* cascadeFarPlanes = AccessByOffset<float*>(basePtr, PredefinedDirectionalLightOffsets.cascadeFarPlanesOffset);
Matrix4f* cascadeViewProj = AccessByOffset<Matrix4f*>(basePtr, PredefinedDirectionalLightOffsets.cascadeViewProjMatricesOffset);
shadowData->GetCascadeData(m_viewer, SparsePtr<float>(cascadeFarPlanes, 4*sizeof(float)), SparsePtr(cascadeViewProj));
AccessByOffset<UInt32&>(basePtr, PredefinedDirectionalLightOffsets.cascadeCountOffset) = SafeCast<UInt32>(shadowData->GetCascadeCount());
}
if (m_renderState.shadowMapsDirectional[i] != shadowmap)
{
m_renderState.shadowMapsDirectional[i] = shadowmap;
InvalidateElements();
}
}
}
void ForwardPipelinePass::PreparePointLights(void* lightMemory)
{
std::size_t lightCount = std::min(m_pointLights.size(), PredefinedLightData::MaxLightCount);
AccessByOffset<UInt32&>(lightMemory, PredefinedLightOffsets.pointLightCountOffset) = SafeCast<UInt32>(lightCount);
for (std::size_t i = 0; i < lightCount; ++i)
{
UInt8* basePtr = static_cast<UInt8*>(lightMemory) + PredefinedLightOffsets.pointLightsOffset + PredefinedPointLightOffsets.totalSize * i;
const PointLight* light = m_pointLights[i].light;
const Color& lightColor = light->GetColor();
AccessByOffset<Vector3f&>(basePtr, PredefinedPointLightOffsets.colorOffset) = Vector3f(lightColor.r, lightColor.g, lightColor.b);
AccessByOffset<Vector3f&>(basePtr, PredefinedPointLightOffsets.positionOffset) = light->GetPosition();
AccessByOffset<Vector2f&>(basePtr, PredefinedPointLightOffsets.invShadowMapSizeOffset) = (light->IsShadowCaster()) ? Vector2f(1.f / light->GetShadowMapSize()) : Vector2f(-1.f, -1.f);
AccessByOffset<float&>(basePtr, PredefinedPointLightOffsets.ambientFactorOffset) = light->GetAmbientFactor();
AccessByOffset<float&>(basePtr, PredefinedPointLightOffsets.diffuseFactorOffset) = light->GetDiffuseFactor();
AccessByOffset<float&>(basePtr, PredefinedPointLightOffsets.radiusOffset) = light->GetRadius();
AccessByOffset<float&>(basePtr, PredefinedPointLightOffsets.invRadiusOffset) = light->GetInvRadius();
// Shadowmap handling
const Texture* shadowmap = m_pipeline.RetrieveLightShadowmap(m_pointLights[i].lightIndex, m_viewer);
if (m_renderState.shadowMapsPoint[i] != shadowmap)
{
m_renderState.shadowMapsPoint[i] = shadowmap;
InvalidateElements();
}
}
}
void ForwardPipelinePass::PrepareSpotLights(void* lightMemory)
{
std::size_t lightCount = std::min(m_spotLights.size(), PredefinedLightData::MaxLightCount);
AccessByOffset<UInt32&>(lightMemory, PredefinedLightOffsets.spotLightCountOffset) = SafeCast<UInt32>(lightCount);
for (std::size_t i = 0; i < lightCount; ++i)
{
UInt8* basePtr = static_cast<UInt8*>(lightMemory) + PredefinedLightOffsets.spotLightsOffset + PredefinedSpotLightOffsets.totalSize * i;
const SpotLight* light = m_spotLights[i].light;
const Color& lightColor = light->GetColor();
AccessByOffset<Vector3f&>(basePtr, PredefinedSpotLightOffsets.colorOffset) = Vector3f(lightColor.r, lightColor.g, lightColor.b);
AccessByOffset<Vector3f&>(basePtr, PredefinedSpotLightOffsets.directionOffset) = light->GetDirection();
AccessByOffset<Vector3f&>(basePtr, PredefinedSpotLightOffsets.positionOffset) = light->GetPosition();
AccessByOffset<Vector2f&>(basePtr, PredefinedSpotLightOffsets.invShadowMapSizeOffset) = (light->IsShadowCaster()) ? Vector2f(1.f / light->GetShadowMapSize()) : Vector2f(-1.f, -1.f);
AccessByOffset<float&>(basePtr, PredefinedSpotLightOffsets.ambientFactorOffset) = light->GetAmbientFactor();
AccessByOffset<float&>(basePtr, PredefinedSpotLightOffsets.diffuseFactorOffset) = light->GetDiffuseFactor();
AccessByOffset<float&>(basePtr, PredefinedSpotLightOffsets.innerAngleOffset) = light->GetInnerAngleCos();
AccessByOffset<float&>(basePtr, PredefinedSpotLightOffsets.outerAngleOffset) = light->GetOuterAngleCos();
AccessByOffset<float&>(basePtr, PredefinedSpotLightOffsets.invRadiusOffset) = light->GetInvRadius();
AccessByOffset<Matrix4f&>(basePtr, PredefinedSpotLightOffsets.viewProjMatrixOffset) = light->GetViewProjMatrix();
// Shadowmap handling
const Texture* shadowmap = m_pipeline.RetrieveLightShadowmap(m_spotLights[i].lightIndex, m_viewer);
if (m_renderState.shadowMapsSpot[i] != shadowmap)
{
m_renderState.shadowMapsSpot[i] = shadowmap;
InvalidateElements();
}
}
}
void ForwardPipelinePass::PrepareLights(RenderFrame& renderFrame, const Frustumf& frustum, const Bitset<UInt64>& visibleLights)
{
// Select lights
m_directionalLights.clear();
m_pointLights.clear();
m_spotLights.clear();
for (std::size_t lightIndex = visibleLights.FindFirst(); lightIndex != visibleLights.npos; lightIndex = visibleLights.FindNext(lightIndex))
{
const Light* light = m_pipeline.RetrieveLight(lightIndex);
switch (light->GetLightType())
{
case UnderlyingCast(BasicLightType::Directional):
m_directionalLights.push_back({ SafeCast<const DirectionalLight*>(light), lightIndex, 0.f });
break;
case UnderlyingCast(BasicLightType::Point):
m_pointLights.push_back({ SafeCast<const PointLight*>(light), lightIndex, light->ComputeContributionScore(frustum) });
break;
case UnderlyingCast(BasicLightType::Spot):
m_spotLights.push_back({ SafeCast<const SpotLight*>(light), lightIndex, light->ComputeContributionScore(frustum) });
break;
}
}
// Sort lights
std::sort(m_pointLights.begin(), m_pointLights.end(), [&](const RenderableLight<PointLight>& lhs, const RenderableLight<PointLight>& rhs)
{
return lhs.contributionScore < rhs.contributionScore;
});
std::sort(m_spotLights.begin(), m_spotLights.end(), [&](const RenderableLight<SpotLight>& lhs, const RenderableLight<SpotLight>& rhs)
{
return lhs.contributionScore < rhs.contributionScore;
});
UploadPool& uploadPool = renderFrame.GetUploadPool();
auto& lightAllocation = uploadPool.Allocate(m_lightDataBuffer->GetSize());
PrepareDirectionalLights(lightAllocation.mappedPtr);
PreparePointLights(lightAllocation.mappedPtr);
PrepareSpotLights(lightAllocation.mappedPtr);
m_pendingLightUploadAllocation = &lightAllocation;
m_pipeline.QueueTransfer(this);
}
} }

View File

@ -240,6 +240,7 @@ namespace Nz
{ {
std::size_t depthPassIndex = m_materialPassRegistry.GetPassIndex("DepthPass"); std::size_t depthPassIndex = m_materialPassRegistry.GetPassIndex("DepthPass");
std::size_t shadowPassIndex = m_materialPassRegistry.GetPassIndex("ShadowPass"); std::size_t shadowPassIndex = m_materialPassRegistry.GetPassIndex("ShadowPass");
std::size_t distanceShadowPassIndex = m_materialPassRegistry.GetPassIndex("DistanceShadowPass");
std::size_t forwardPassIndex = m_materialPassRegistry.GetPassIndex("ForwardPass"); std::size_t forwardPassIndex = m_materialPassRegistry.GetPassIndex("ForwardPass");
// BasicMaterial // BasicMaterial
@ -257,9 +258,12 @@ namespace Nz
settings.AddPass(depthPassIndex, depthPass); settings.AddPass(depthPassIndex, depthPass);
MaterialPass shadowPass = depthPass; MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
settings.AddPass(shadowPassIndex, shadowPass); settings.AddPass(shadowPassIndex, shadowPass);
MaterialPass distanceShadowPass = shadowPass;
distanceShadowPass.options[CRC32("DistanceDepth")] = true;
settings.AddPass(distanceShadowPassIndex, distanceShadowPass);
m_defaultMaterials.materials[MaterialType::Basic].material = std::make_shared<Material>(std::move(settings), "BasicMaterial"); m_defaultMaterials.materials[MaterialType::Basic].material = std::make_shared<Material>(std::move(settings), "BasicMaterial");
} }
@ -279,9 +283,12 @@ namespace Nz
settings.AddPass(depthPassIndex, depthPass); settings.AddPass(depthPassIndex, depthPass);
MaterialPass shadowPass = depthPass; MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
settings.AddPass(shadowPassIndex, shadowPass); settings.AddPass(shadowPassIndex, shadowPass);
MaterialPass distanceShadowPass = shadowPass;
distanceShadowPass.options[CRC32("DistanceDepth")] = true;
settings.AddPass(distanceShadowPassIndex, distanceShadowPass);
m_defaultMaterials.materials[MaterialType::PhysicallyBased].material = std::make_shared<Material>(std::move(settings), "PhysicallyBasedMaterial"); m_defaultMaterials.materials[MaterialType::PhysicallyBased].material = std::make_shared<Material>(std::move(settings), "PhysicallyBasedMaterial");
} }
@ -301,12 +308,12 @@ namespace Nz
settings.AddPass(depthPassIndex, depthPass); settings.AddPass(depthPassIndex, depthPass);
MaterialPass shadowPass = depthPass; MaterialPass shadowPass = depthPass;
shadowPass.states.faceCulling = FaceCulling::Front;
shadowPass.states.depthBias = true;
shadowPass.states.depthBiasConstantFactor = 0.005f;
shadowPass.states.depthBiasSlopeFactor = 0.05f;
settings.AddPass(shadowPassIndex, shadowPass); settings.AddPass(shadowPassIndex, shadowPass);
MaterialPass distanceShadowPass = shadowPass;
distanceShadowPass.options[CRC32("DistanceDepth")] = true;
settings.AddPass(distanceShadowPassIndex, distanceShadowPass);
m_defaultMaterials.materials[MaterialType::Phong].material = std::make_shared<Material>(std::move(settings), "PhongMaterial"); m_defaultMaterials.materials[MaterialType::Phong].material = std::make_shared<Material>(std::move(settings), "PhongMaterial");
} }
@ -399,6 +406,7 @@ namespace Nz
m_materialPassRegistry.RegisterPass("ForwardPass"); m_materialPassRegistry.RegisterPass("ForwardPass");
m_materialPassRegistry.RegisterPass("DepthPass"); m_materialPassRegistry.RegisterPass("DepthPass");
m_materialPassRegistry.RegisterPass("ShadowPass"); m_materialPassRegistry.RegisterPass("ShadowPass");
m_materialPassRegistry.RegisterPass("DistanceShadowPass");
} }
void Graphics::RegisterShaderModules() void Graphics::RegisterShaderModules()

View File

@ -8,4 +8,12 @@
namespace Nz namespace Nz
{ {
LightShadowData::~LightShadowData() = default; LightShadowData::~LightShadowData() = default;
void LightShadowData::RegisterViewer(const AbstractViewer* /*viewer*/)
{
}
void LightShadowData::UnregisterViewer(const AbstractViewer* /*viewer*/)
{
}
} }

View File

@ -37,6 +37,7 @@ namespace Nz
options.partialSanitization = true; options.partialSanitization = true;
options.moduleResolver = graphics->GetShaderModuleResolver(); options.moduleResolver = graphics->GetShaderModuleResolver();
options.optionValues[CRC32("MaxLightCount")] = SafeCast<UInt32>(PredefinedLightData::MaxLightCount); options.optionValues[CRC32("MaxLightCount")] = SafeCast<UInt32>(PredefinedLightData::MaxLightCount);
options.optionValues[CRC32("MaxLightCascadeCount")] = SafeCast<UInt32>(PredefinedDirectionalLightData::MaxLightCascadeCount);
options.optionValues[CRC32("MaxJointCount")] = SafeCast<UInt32>(PredefinedSkeletalData::MaxMatricesCount); options.optionValues[CRC32("MaxJointCount")] = SafeCast<UInt32>(PredefinedSkeletalData::MaxMatricesCount);
nzsl::Ast::ModulePtr sanitizedModule = nzsl::Ast::Sanitize(*referenceModule, options); nzsl::Ast::ModulePtr sanitizedModule = nzsl::Ast::Sanitize(*referenceModule, options);
@ -94,11 +95,14 @@ namespace Nz
if (auto it = block->uniformBlocks.find("ViewerData"); it != block->uniformBlocks.end()) if (auto it = block->uniformBlocks.find("ViewerData"); it != block->uniformBlocks.end())
m_engineShaderBindings[EngineShaderBinding::ViewerDataUbo] = it->second.bindingIndex; m_engineShaderBindings[EngineShaderBinding::ViewerDataUbo] = it->second.bindingIndex;
if (auto it = block->samplers.find("ShadowMaps2D"); it != block->samplers.end()) if (auto it = block->samplers.find("ShadowMapsDirectional"); it != block->samplers.end())
m_engineShaderBindings[EngineShaderBinding::Shadowmap2D] = it->second.bindingIndex; m_engineShaderBindings[EngineShaderBinding::ShadowmapDirectional] = it->second.bindingIndex;
if (auto it = block->samplers.find("ShadowMapsCube"); it != block->samplers.end()) if (auto it = block->samplers.find("ShadowMapsPoint"); it != block->samplers.end())
m_engineShaderBindings[EngineShaderBinding::ShadowmapCube] = it->second.bindingIndex; m_engineShaderBindings[EngineShaderBinding::ShadowmapPoint] = it->second.bindingIndex;
if (auto it = block->samplers.find("ShadowMapsSpot"); it != block->samplers.end())
m_engineShaderBindings[EngineShaderBinding::ShadowmapSpot] = it->second.bindingIndex;
if (auto it = block->uniformBlocks.find("SkeletalData"); it != block->uniformBlocks.end()) if (auto it = block->uniformBlocks.find("SkeletalData"); it != block->uniformBlocks.end())
m_engineShaderBindings[EngineShaderBinding::SkeletalDataUbo] = it->second.bindingIndex; m_engineShaderBindings[EngineShaderBinding::SkeletalDataUbo] = it->second.bindingIndex;

View File

@ -3,33 +3,20 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/PointLight.hpp> #include <Nazara/Graphics/PointLight.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/PointLightShadowData.hpp> #include <Nazara/Graphics/PointLightShadowData.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Math/Vector4.hpp>
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
namespace Nz namespace Nz
{ {
float PointLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const float PointLight::ComputeContributionScore(const Frustumf& viewerFrustum) const
{ {
// TODO: take luminosity/radius into account // TODO: take luminosity/radius into account
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter()); return viewerFrustum.GetPlane(FrustumPlane::Near).SignedDistance(m_position);
} }
void PointLight::FillLightData(void* data) const bool PointLight::FrustumCull(const Frustumf& viewerFrustum) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); return viewerFrustum.Intersect(Spheref(m_position, m_radius)) != IntersectionSide::Outside;
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Point);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r, m_color.g, m_color.b, m_color.a);
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_position.x, m_position.y, m_position.z, 0.f);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_radius, m_invRadius, 0.f, 0.f);
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = (IsShadowCaster()) ? Vector2f(1.f / GetShadowMapSize()) : Vector2f(-1.f, -1.f);
} }
std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const

View File

@ -61,7 +61,7 @@ namespace Nz
} }
}); });
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass"); std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DistanceShadowPass");
constexpr float zNear = 0.01f; constexpr float zNear = 0.01f;
@ -93,8 +93,10 @@ namespace Nz
}); });
} }
void PointLightShadowData::PrepareRendering(RenderFrame& renderFrame) void PointLightShadowData::PrepareRendering(RenderFrame& renderFrame, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
for (DirectionData& direction : m_directions) for (DirectionData& direction : m_directions)
{ {
const Matrix4f& viewProjMatrix = direction.viewer.GetViewerInstance().GetViewProjMatrix(); const Matrix4f& viewProjMatrix = direction.viewer.GetViewerInstance().GetViewProjMatrix();
@ -114,8 +116,10 @@ namespace Nz
direction.depthPass->RegisterMaterialInstance(matInstance); direction.depthPass->RegisterMaterialInstance(matInstance);
} }
void PointLightShadowData::RegisterPassInputs(FramePass& pass) void PointLightShadowData::RegisterPassInputs(FramePass& pass, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
std::size_t cubeInputIndex = pass.AddInput(m_cubeAttachmentIndex); std::size_t cubeInputIndex = pass.AddInput(m_cubeAttachmentIndex);
pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput); pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput);
@ -123,8 +127,10 @@ namespace Nz
pass.AddInput(direction.attachmentIndex); pass.AddInput(direction.attachmentIndex);
} }
void PointLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph) void PointLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
UInt32 shadowMapSize = m_light.GetShadowMapSize(); UInt32 shadowMapSize = m_light.GetShadowMapSize();
m_cubeAttachmentIndex = frameGraph.AddAttachmentCube({ m_cubeAttachmentIndex = frameGraph.AddAttachmentCube({
@ -142,7 +148,7 @@ namespace Nz
} }
} }
const Texture* PointLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const const Texture* PointLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* /*viewer*/) const
{ {
return bakedGraph.GetAttachmentTexture(m_cubeAttachmentIndex).get(); return bakedGraph.GetAttachmentTexture(m_cubeAttachmentIndex).get();
} }

View File

@ -1,26 +1,61 @@
[nzsl_version("1.0")] [nzsl_version("1.0")]
module Engine.LightData; module Engine.LightData;
option MaxLightCascadeCount: u32 = u32(4); //< FIXME: Fix integral value types
option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types
[export] [export]
[layout(std140)] [layout(std140)]
struct Light struct DirectionalLight
{ {
type: i32, color: vec3[f32],
color: vec4[f32], direction: vec3[f32],
factor: vec2[f32],
parameter1: vec4[f32],
parameter2: vec4[f32],
parameter3: vec4[f32],
invShadowMapSize: vec2[f32], invShadowMapSize: vec2[f32],
viewProjMatrix: mat4[f32] ambientFactor: f32,
diffuseFactor: f32,
cascadeCount: u32,
cascadeDistances: array[f32, MaxLightCascadeCount],
viewProjMatrices: array[mat4[f32], MaxLightCascadeCount],
}
[export]
[layout(std140)]
struct PointLight
{
color: vec3[f32],
position: vec3[f32],
invShadowMapSize: vec2[f32],
ambientFactor: f32,
diffuseFactor: f32,
radius: f32,
invRadius: f32,
}
[export]
[layout(std140)]
struct SpotLight
{
color: vec3[f32],
direction: vec3[f32],
position: vec3[f32],
invShadowMapSize: vec2[f32],
ambientFactor: f32,
diffuseFactor: f32,
innerAngle: f32,
outerAngle: f32,
invRadius: f32,
viewProjMatrix: mat4[f32],
} }
[export] [export]
[layout(std140)] [layout(std140)]
struct LightData struct LightData
{ {
lights: array[Light, MaxLightCount], directionalLights: array[DirectionalLight, MaxLightCount],
lightCount: u32, pointLights: array[PointLight, MaxLightCount],
spotLights: array[SpotLight, MaxLightCount],
directionalLightCount: u32,
pointLightCount: u32,
spotLightCount: u32,
} }

View File

@ -10,6 +10,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear;
// Pass-specific options // Pass-specific options
option DepthPass: bool = false; option DepthPass: bool = false;
option DistanceDepth: bool = false;
// Basic material options // Basic material options
option HasBaseColorTexture: bool = false; option HasBaseColorTexture: bool = false;
@ -39,6 +40,7 @@ option VertexUvLoc: i32 = -1;
option VertexJointIndicesLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1;
option VertexJointWeightsLoc: i32 = -1; option VertexJointWeightsLoc: i32 = -1;
option MaxLightCascadeCount: u32 = u32(4); //< FIXME: Fix integral value types
option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types
const HasNormal = (VertexNormalLoc >= 0); const HasNormal = (VertexNormalLoc >= 0);
@ -98,8 +100,9 @@ external
[tag("ViewerData")] viewerData: uniform[ViewerData], [tag("ViewerData")] viewerData: uniform[ViewerData],
[tag("SkeletalData")] skeletalData: uniform[SkeletalData], [tag("SkeletalData")] skeletalData: uniform[SkeletalData],
[tag("LightData")] lightData: uniform[LightData], [tag("LightData")] lightData: uniform[LightData],
[tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount], [tag("ShadowMapsDirectional")] shadowMapsDirectional: array[depth_sampler2D_array[f32], MaxLightCount],
[tag("ShadowMapsCube")] shadowMapsCube: array[sampler_cube[f32], MaxLightCount] [tag("ShadowMapsPoint")] shadowMapsPoint: array[sampler_cube[f32], MaxLightCount],
[tag("ShadowMapsSpot")] shadowMapsSpot: array[depth_sampler2D[f32], MaxLightCount],
} }
struct VertToFrag struct VertToFrag
@ -109,14 +112,15 @@ struct VertToFrag
[location(2), cond(HasColor)] color: vec4[f32], [location(2), cond(HasColor)] color: vec4[f32],
[location(3), cond(HasNormal)] normal: vec3[f32], [location(3), cond(HasNormal)] normal: vec3[f32],
[location(4), cond(HasNormalMapping)] tangent: vec3[f32], [location(4), cond(HasNormalMapping)] tangent: vec3[f32],
[location(5), cond(HasLighting)] lightProjPos: array[vec4[f32], MaxLightCount],
[builtin(position)] position: vec4[f32], [builtin(position)] position: vec4[f32],
[builtin(frag_coord)] fragcoord: vec4[f32]
} }
// Fragment stage // Fragment stage
struct FragOut struct FragOut
{ {
[location(0)] RenderTarget0: vec4[f32] [location(0)] RenderTarget0: vec4[f32],
[builtin(frag_depth), cond(DistanceDepth)] fragdepth: f32,
} }
fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32 fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32
@ -124,8 +128,7 @@ fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32
return zNear * zFar / (zFar + depth * (zNear - zFar)); return zNear * zFar / (zFar + depth * (zNear - zFar));
} }
[entry(frag), cond(!DepthPass || AlphaTest)] fn ComputeColor(input: VertToFrag) -> vec4[f32]
fn main(input: VertToFrag) -> FragOut
{ {
let color = settings.BaseColor; let color = settings.BaseColor;
@ -141,6 +144,13 @@ fn main(input: VertToFrag) -> FragOut
const if (HasAlphaTexture) const if (HasAlphaTexture)
color.w *= MaterialAlphaMap.Sample(input.uv).x; color.w *= MaterialAlphaMap.Sample(input.uv).x;
return color;
}
[entry(frag), cond(!DepthPass)]
fn main(input: VertToFrag) -> FragOut
{
let color = ComputeColor(input);
const if (AlphaTest) const if (AlphaTest)
{ {
if (color.w < settings.AlphaThreshold) if (color.w < settings.AlphaThreshold)
@ -168,139 +178,177 @@ fn main(input: VertToFrag) -> FragOut
else else
normal = normalize(input.normal); normal = normalize(input.normal);
for i in u32(0) -> lightData.lightCount for lightIndex in u32(0) -> lightData.directionalLightCount
{ {
let light = lightData.lights[i]; let light = lightData.directionalLights[lightIndex];
let lightAmbientFactor = light.factor.x; let lambert = max(dot(normal, -light.direction), 0.0);
let lightDiffuseFactor = light.factor.y;
// TODO: Add switch instruction let reflection = reflect(light.direction, normal);
if (light.type == DirectionalLight) let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
let shadowFactor = 1.0;
const if (EnableShadowMapping)
{ {
let lightDir = light.parameter1.xyz; if (light.invShadowMapSize.x > 0.0)
lightAmbient += light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
let lambert = max(dot(normal, -lightDir), 0.0);
lightDiffuse += lambert * light.color.rgb * lightDiffuseFactor;
let reflection = reflect(lightDir, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
lightSpecular += specFactor * light.color.rgb;
}
else if (light.type == PointLight)
{
let lightPos = light.parameter1.xyz;
let lightRadius = light.parameter2.x;
let lightInvRadius = light.parameter2.y;
let lightToPos = input.worldPos - lightPos;
let dist = length(lightToPos);
let lightToPosNorm = lightToPos / max(dist, 0.0001);
let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0);
let lambert = max(dot(normal, -lightToPosNorm), 0.0);
let reflection = reflect(lightToPosNorm, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
let shadowFactor = 1.0;
const if (EnableShadowMapping)
{ {
if (light.invShadowMapSize.x > 0.0) let fragPosViewSpace = viewerData.viewMatrix * vec4[f32](input.worldPos, 1.0);
let depthValue = abs(fragPosViewSpace.z);
let cascadeIndex = MaxLightCascadeCount;
for index in u32(0) -> light.cascadeCount
{ {
shadowFactor = 0.0; if (depthValue < light.cascadeDistances[index])
{
cascadeIndex = index;
break;
}
}
let sampleDir = vec3[f32](lightToPosNorm.x, lightToPosNorm.y, -lightToPosNorm.z); if (cascadeIndex >= light.cascadeCount)
cascadeIndex = light.cascadeCount - u32(1);
const sampleCount = 4; let lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](input.worldPos, 1.0);
const offset = 0.005; let shadowCoords = lightProjPos.xyz / lightProjPos.w;
const invSampleCount = 1.0 / f32(sampleCount);
const start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5);
const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount);
// calculate bias (based on depth map resolution and slope)
let bias = max(0.05 * (1.0 - lambert), 0.005);
bias *= 1.0 / (light.cascadeDistances[cascadeIndex] * 0.05);
//shadowFactor = shadowMapsDirectional[lightIndex].SampleDepthComp(vec3[f32](shadowCoords.xy, f32(cascadeIndex)), shadowCoords.z).r;
shadowFactor = 0.0;
[unroll]
for x in -1 -> 2
{
[unroll] [unroll]
for x in 0 -> sampleCount for y in -1 -> 2
{
let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize;
shadowFactor += shadowMapsDirectional[lightIndex].SampleDepthComp(vec3[f32](coords, f32(cascadeIndex)), shadowCoords.z - bias).r;
}
}
shadowFactor /= 9.0;
}
}
lightAmbient += shadowFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb;
lightDiffuse += shadowFactor * lambert * light.color.rgb * light.diffuseFactor;
lightSpecular += shadowFactor * specFactor * light.color.rgb;
}
for lightIndex in u32(0) -> lightData.pointLightCount
{
let light = lightData.pointLights[lightIndex];
let lightToPos = input.worldPos - light.position;
let dist = length(lightToPos);
let lightToPosNorm = lightToPos / max(dist, 0.0001);
let attenuationFactor = max(1.0 - dist * light.invRadius, 0.0);
let lambert = clamp(dot(normal, -lightToPosNorm), 0.0, 1.0);
let reflection = reflect(lightToPosNorm, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
let shadowFactor = 1.0;
const if (EnableShadowMapping)
{
if (light.invShadowMapSize.x > 0.0)
{
shadowFactor = 0.0;
let sampleDir = vec3[f32](lightToPosNorm.x, lightToPosNorm.y, -lightToPosNorm.z);
const sampleCount = 4;
const offset = 0.005;
const invSampleCount = 1.0 / f32(sampleCount);
const start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5);
const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount);
let bias = 0.05;
[unroll]
for x in 0 -> sampleCount
{
[unroll]
for y in 0 -> sampleCount
{ {
[unroll] [unroll]
for y in 0 -> sampleCount for z in 0 -> sampleCount
{ {
[unroll] let dirOffset = vec3[f32](f32(x), f32(y), f32(z)) * invSampleCount * offset - start;
for z in 0 -> sampleCount let sampleDir = sampleDir + dirOffset;
{
let dirOffset = vec3[f32](f32(x), f32(y), f32(z)) * invSampleCount * offset - start;
let sampleDir = sampleDir + dirOffset;
let depth = shadowMapsCube[i].Sample(sampleDir).r; let closestDepth = shadowMapsPoint[lightIndex].Sample(sampleDir).r;
depth = LinearizeDepth(depth, 0.01, lightRadius); closestDepth *= light.radius;
if (depth > dist) if (closestDepth > dist - bias)
shadowFactor += shadowContribution; shadowFactor += shadowContribution;
}
} }
} }
} }
} }
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor;
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
} }
else if (light.type == SpotLight)
lightAmbient += attenuationFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb;
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * light.diffuseFactor;
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
}
for lightIndex in u32(0) -> lightData.spotLightCount
{
let light = lightData.spotLights[lightIndex];
let lightToPos = input.worldPos - light.position;
let dist = length(lightToPos);
let lightToPosNorm = lightToPos / max(dist, 0.0001);
let curAngle = dot(light.direction, lightToPosNorm);
let innerMinusOuterAngle = light.innerAngle - light.outerAngle;
let attenuationFactor = max(1.0 - dist * light.invRadius, 0.0);
attenuationFactor *= max((curAngle - light.outerAngle) / innerMinusOuterAngle, 0.0);
let lambert = clamp(dot(normal, -lightToPosNorm), 0.0, 1.0);
let reflection = reflect(lightToPosNorm, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
let shadowFactor = 1.0;
const if (EnableShadowMapping)
{ {
let lightPos = light.parameter1.xyz; if (light.invShadowMapSize.x > 0.0)
let lightDir = light.parameter2.xyz;
let lightInvRadius = light.parameter1.w;
let lightInnerAngle = light.parameter3.x;
let lightOuterAngle = light.parameter3.y;
let lightToPos = input.worldPos - lightPos;
let dist = length(lightToPos);
let lightToPosNorm = lightToPos / max(dist, 0.0001);
let curAngle = dot(lightDir, lightToPosNorm);
let innerMinusOuterAngle = lightInnerAngle - lightOuterAngle;
let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0);
attenuationFactor *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0);
let lambert = max(dot(normal, -lightToPosNorm), 0.0);
let reflection = reflect(lightToPosNorm, normal);
let specFactor = max(dot(reflection, eyeVec), 0.0);
specFactor = pow(specFactor, settings.Shininess);
let shadowFactor = 1.0;
const if (EnableShadowMapping)
{ {
if (light.invShadowMapSize.x > 0.0) let bias = 0.0005 * tan(acos(lambert));
{ bias = clamp(bias, 0.0, 0.01);
let shadowCoords = input.lightProjPos[i].xyz / input.lightProjPos[i].w;
shadowFactor = 0.0;
[unroll]
for x in -1 -> 2
{
[unroll]
for y in -1 -> 2
{
let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize;
shadowFactor += shadowMaps2D[i].SampleDepthComp(coords, shadowCoords.z).r;
}
}
shadowFactor /= 9.0;
}
}
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb; let lightProjPos = light.viewProjMatrix * vec4[f32](input.worldPos, 1.0);
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor; let shadowCoords = lightProjPos.xyz / lightProjPos.w;
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
shadowFactor = 0.0;
[unroll]
for x in -1 -> 2
{
[unroll]
for y in -1 -> 2
{
let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize;
shadowFactor += shadowMapsSpot[lightIndex].SampleDepthComp(coords, shadowCoords.z - bias).r;
}
}
shadowFactor /= 9.0;
}
} }
lightAmbient += attenuationFactor * light.color.rgb * light.ambientFactor * settings.AmbientColor.rgb;
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * light.diffuseFactor;
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
} }
lightSpecular *= settings.SpecularColor.rgb; lightSpecular *= settings.SpecularColor.rgb;
@ -310,22 +358,50 @@ fn main(input: VertToFrag) -> FragOut
let lightColor = lightAmbient + lightDiffuse + lightSpecular; let lightColor = lightAmbient + lightDiffuse + lightSpecular;
let output: FragOut; color.rgb *= lightColor;
output.RenderTarget0 = vec4[f32](lightColor, 1.0) * color;
return output;
}
else
{
let output: FragOut;
output.RenderTarget0 = color;
return output;
} }
let output: FragOut;
output.RenderTarget0 = color;
return output;
} }
// Dummy fragment shader (TODO: Add a way to delete stage?) // Shadow passes entries
[entry(frag), cond(DepthPass && !AlphaTest)] [entry(frag), cond(DepthPass && DistanceDepth)]
fn main() {} [depth_write(replace)]
fn main(input: VertToFrag) -> FragOut
{
let color = ComputeColor(input);
const if (AlphaTest)
{
if (color.w < settings.AlphaThreshold)
discard;
}
let output: FragOut;
output.RenderTarget0 = ComputeColor(input);
let dist = distance(viewerData.eyePosition, input.worldPos);
output.fragdepth = dist / viewerData.farPlane;
return output;
}
[entry(frag), cond(DepthPass && AlphaTest && !DistanceDepth)]
fn main(input: VertToFrag) -> FragOut
{
let color = ComputeColor(input);
if (color.w < settings.AlphaThreshold)
discard;
let output: FragOut;
output.RenderTarget0 = color;
return output;
}
[entry(frag), cond(DepthPass && !AlphaTest && !DistanceDepth)]
fn main() {} //< dummy
// Vertex stage // Vertex stage
struct VertIn struct VertIn
@ -432,7 +508,7 @@ fn main(input: VertIn) -> VertToFrag
output.worldPos = worldPosition.xyz; output.worldPos = worldPosition.xyz;
output.position = viewerData.viewProjMatrix * worldPosition; output.position = viewerData.viewProjMatrix * worldPosition;
let rotationMatrix = transpose(inverse(mat3[f32](instanceData.worldMatrix))); const if (HasNormal || HasNormalMapping) let rotationMatrix = transpose(inverse(mat3[f32](instanceData.worldMatrix)));
const if (HasColor) const if (HasColor)
output.color = input.color; output.color = input.color;
@ -446,11 +522,5 @@ fn main(input: VertIn) -> VertToFrag
const if (HasNormalMapping) const if (HasNormalMapping)
output.tangent = rotationMatrix * input.tangent; output.tangent = rotationMatrix * input.tangent;
const if (HasLighting)
{
for i in u32(0) -> lightData.lightCount
output.lightProjPos[i] = lightData.lights[i].viewProjMatrix * worldPosition;
}
return output; return output;
} }

View File

@ -170,7 +170,7 @@ fn main(input: VertToFrag) -> FragOut
let albedoFactor = albedo / Pi; let albedoFactor = albedo / Pi;
for i in u32(0) -> lightData.lightCount /*for i in u32(0) -> lightData.lightCount
{ {
let light = lightData.lights[i]; let light = lightData.lights[i];
@ -224,7 +224,7 @@ fn main(input: VertToFrag) -> FragOut
let NdotL = max(dot(normal, lightToPosNorm), 0.0); let NdotL = max(dot(normal, lightToPosNorm), 0.0);
lightRadiance += (diffuse * albedoFactor + specular) * radiance * NdotL; lightRadiance += (diffuse * albedoFactor + specular) * radiance * NdotL;
} }*/
let ambient = (0.03).rrr * albedo; let ambient = (0.03).rrr * albedo;

View File

@ -3,35 +3,37 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/SpotLight.hpp> #include <Nazara/Graphics/SpotLight.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
#include <Nazara/Graphics/SpotLightShadowData.hpp> #include <Nazara/Graphics/SpotLightShadowData.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Math/Vector4.hpp>
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
namespace Nz namespace Nz
{ {
float SpotLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const float SpotLight::ComputeContributionScore(const Frustumf& viewerFrustum) const
{ {
// TODO: take luminosity/radius/direction into account // TODO: take luminosity/radius into account
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter()); return viewerFrustum.GetPlane(FrustumPlane::Near).SignedDistance(m_position);
} }
void SpotLight::FillLightData(void* data) const bool SpotLight::FrustumCull(const Frustumf& viewerFrustum) const
{ {
auto lightOffset = PredefinedLightData::GetOffsets(); // We need the radius of the projected circle depending on the distance
// Tangent = Opposite/Adjacent <=> Opposite = Adjacent * Tangent
float opposite = m_radius * m_outerAngleTan;
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Spot); Vector3f base = Vector3f::Forward() * m_radius;
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.color) = Vector4f(m_color.r, m_color.g, m_color.b, m_color.a); Vector3f lExtend = Vector3f::Left() * opposite;
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.factor) = Vector2f(m_ambientFactor, m_diffuseFactor); Vector3f uExtend = Vector3f::Up() * opposite;
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter1) = Vector4f(m_position.x, m_position.y, m_position.z, m_invRadius);
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f); // Test five points against frustum
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter3) = Vector4f(m_innerAngleCos, m_outerAngleCos, 0.f, 0.f); std::array<Vector3f, 5> points = {
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = (IsShadowCaster()) ? Vector2f(1.f / GetShadowMapSize()) : Vector2f(-1.f, -1.f); m_position,
AccessByOffset<Matrix4f&>(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix; base + lExtend + uExtend,
base + lExtend - uExtend,
base - lExtend + uExtend,
base - lExtend - uExtend,
};
return viewerFrustum.Contains(points.data(), points.size());
} }
std::unique_ptr<LightShadowData> SpotLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const std::unique_ptr<LightShadowData> SpotLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const

View File

@ -8,6 +8,7 @@
#include <Nazara/Graphics/FramePipeline.hpp> #include <Nazara/Graphics/FramePipeline.hpp>
#include <Nazara/Graphics/Graphics.hpp> #include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/SpotLight.hpp> #include <Nazara/Graphics/SpotLight.hpp>
#include <cassert>
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
namespace Nz namespace Nz
@ -54,8 +55,10 @@ namespace Nz
}); });
} }
void SpotLightShadowData::PrepareRendering(RenderFrame& renderFrame) void SpotLightShadowData::PrepareRendering(RenderFrame& renderFrame, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
const Matrix4f& viewProjMatrix = m_viewer.GetViewerInstance().GetViewProjMatrix(); const Matrix4f& viewProjMatrix = m_viewer.GetViewerInstance().GetViewProjMatrix();
Frustumf frustum = Frustumf::Extract(viewProjMatrix); Frustumf frustum = Frustumf::Extract(viewProjMatrix);
@ -71,13 +74,17 @@ namespace Nz
m_depthPass->RegisterMaterialInstance(matInstance); m_depthPass->RegisterMaterialInstance(matInstance);
} }
void SpotLightShadowData::RegisterPassInputs(FramePass& pass) void SpotLightShadowData::RegisterPassInputs(FramePass& pass, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
pass.AddInput(m_attachmentIndex); pass.AddInput(m_attachmentIndex);
} }
void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph) void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph, [[maybe_unused]] const AbstractViewer* viewer)
{ {
assert(viewer == nullptr);
UInt32 shadowMapSize = m_light.GetShadowMapSize(); UInt32 shadowMapSize = m_light.GetShadowMapSize();
m_attachmentIndex = frameGraph.AddAttachment({ m_attachmentIndex = frameGraph.AddAttachment({
@ -90,7 +97,7 @@ namespace Nz
m_depthPass->RegisterToFrameGraph(frameGraph, m_attachmentIndex); m_depthPass->RegisterToFrameGraph(frameGraph, m_attachmentIndex);
} }
const Nz::Texture* SpotLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const const Texture* SpotLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph, const AbstractViewer* /*viewer*/) const
{ {
return bakedGraph.GetAttachmentTexture(m_attachmentIndex).get(); return bakedGraph.GetAttachmentTexture(m_attachmentIndex).get();
} }

View File

@ -56,7 +56,7 @@ namespace Nz
return std::make_unique<SpriteChainRendererData>(); return std::make_unique<SpriteChainRendererData>();
} }
void SpriteChainRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) void SpriteChainRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& currentFrame, std::size_t elementCount, const Pointer<const RenderElement>* elements, SparsePtr<const RenderStates> renderStates)
{ {
Graphics* graphics = Graphics::Instance(); Graphics* graphics = Graphics::Instance();

View File

@ -24,7 +24,7 @@ namespace Nz
return std::make_unique<SubmeshRendererData>(); return std::make_unique<SubmeshRendererData>();
} }
void SubmeshRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& /*currentFrame*/, std::size_t elementCount, const Pointer<const RenderElement>* elements, const RenderStates* renderStates) void SubmeshRenderer::Prepare(const ViewerInstance& viewerInstance, ElementRendererData& rendererData, RenderFrame& /*currentFrame*/, std::size_t elementCount, const Pointer<const RenderElement>* elements, SparsePtr<const RenderStates> renderStates)
{ {
Graphics* graphics = Graphics::Instance(); Graphics* graphics = Graphics::Instance();
@ -55,6 +55,7 @@ namespace Nz
}; };
const auto& depthTexture2D = Graphics::Instance()->GetDefaultTextures().depthTextures[ImageType::E2D]; const auto& depthTexture2D = Graphics::Instance()->GetDefaultTextures().depthTextures[ImageType::E2D];
const auto& depthTexture2DArray = Graphics::Instance()->GetDefaultTextures().depthTextures[ImageType::E2D_Array];
const auto& depthTextureCube = Graphics::Instance()->GetDefaultTextures().depthTextures[ImageType::Cubemap]; const auto& depthTextureCube = Graphics::Instance()->GetDefaultTextures().depthTextures[ImageType::Cubemap];
const auto& whiteTexture2D = Graphics::Instance()->GetDefaultTextures().whiteTextures[ImageType::E2D]; const auto& whiteTexture2D = Graphics::Instance()->GetDefaultTextures().whiteTextures[ImageType::E2D];
const auto& defaultSampler = graphics->GetSamplerCache().Get({}); const auto& defaultSampler = graphics->GetSamplerCache().Get({});
@ -129,7 +130,7 @@ namespace Nz
m_bindingCache.clear(); m_bindingCache.clear();
m_textureBindingCache.clear(); m_textureBindingCache.clear();
m_textureBindingCache.reserve(renderState.shadowMaps2D.size() + renderState.shadowMapsCube.size()); m_textureBindingCache.reserve(renderState.shadowMapsSpot.size() + renderState.shadowMapsDirectional.size() + renderState.shadowMapsPoint.size());
currentMaterialInstance->FillShaderBinding(m_bindingCache); currentMaterialInstance->FillShaderBinding(m_bindingCache);
const Material& material = *currentMaterialInstance->GetParentMaterial(); const Material& material = *currentMaterialInstance->GetParentMaterial();
@ -158,15 +159,15 @@ namespace Nz
}; };
} }
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::Shadowmap2D); bindingIndex != Material::InvalidBindingIndex) if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapDirectional); bindingIndex != Material::InvalidBindingIndex)
{ {
std::size_t textureBindingBaseIndex = m_textureBindingCache.size(); std::size_t textureBindingBaseIndex = m_textureBindingCache.size();
for (std::size_t j = 0; j < renderState.shadowMaps2D.size(); ++j) for (std::size_t j = 0; j < renderState.shadowMapsDirectional.size(); ++j)
{ {
const Texture* texture = renderState.shadowMaps2D[j]; const Texture* texture = renderState.shadowMapsDirectional[j];
if (!texture) if (!texture)
texture = depthTexture2D.get(); texture = depthTexture2DArray.get();
auto& textureEntry = m_textureBindingCache.emplace_back(); auto& textureEntry = m_textureBindingCache.emplace_back();
textureEntry.texture = texture; textureEntry.texture = texture;
@ -176,17 +177,17 @@ namespace Nz
auto& bindingEntry = m_bindingCache.emplace_back(); auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex; bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::SampledTextureBindings { bindingEntry.content = ShaderBinding::SampledTextureBindings {
SafeCast<UInt32>(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex] SafeCast<UInt32>(renderState.shadowMapsDirectional.size()), &m_textureBindingCache[textureBindingBaseIndex]
}; };
} }
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapCube); bindingIndex != Material::InvalidBindingIndex) if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapPoint); bindingIndex != Material::InvalidBindingIndex)
{ {
std::size_t textureBindingBaseIndex = m_textureBindingCache.size(); std::size_t textureBindingBaseIndex = m_textureBindingCache.size();
for (std::size_t j = 0; j < renderState.shadowMapsCube.size(); ++j) for (std::size_t j = 0; j < renderState.shadowMapsPoint.size(); ++j)
{ {
const Texture* texture = renderState.shadowMapsCube[j]; const Texture* texture = renderState.shadowMapsPoint[j];
if (!texture) if (!texture)
texture = depthTextureCube.get(); texture = depthTextureCube.get();
@ -198,7 +199,29 @@ namespace Nz
auto& bindingEntry = m_bindingCache.emplace_back(); auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex; bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::SampledTextureBindings { bindingEntry.content = ShaderBinding::SampledTextureBindings {
SafeCast<UInt32>(renderState.shadowMapsCube.size()), &m_textureBindingCache[textureBindingBaseIndex] SafeCast<UInt32>(renderState.shadowMapsPoint.size()), &m_textureBindingCache[textureBindingBaseIndex]
};
}
if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapSpot); bindingIndex != Material::InvalidBindingIndex)
{
std::size_t textureBindingBaseIndex = m_textureBindingCache.size();
for (std::size_t j = 0; j < renderState.shadowMapsSpot.size(); ++j)
{
const Texture* texture = renderState.shadowMapsSpot[j];
if (!texture)
texture = depthTexture2D.get();
auto& textureEntry = m_textureBindingCache.emplace_back();
textureEntry.texture = texture;
textureEntry.sampler = shadowSampler.get();
}
auto& bindingEntry = m_bindingCache.emplace_back();
bindingEntry.bindingIndex = bindingIndex;
bindingEntry.content = ShaderBinding::SampledTextureBindings {
SafeCast<UInt32>(renderState.shadowMapsSpot.size()), &m_textureBindingCache[textureBindingBaseIndex]
}; };
} }