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:
parent
a08850946a
commit
9aebb4f745
|
|
@ -61,7 +61,6 @@ namespace Nz
|
|||
std::size_t m_lastVisibilityHash;
|
||||
std::string m_passName;
|
||||
std::vector<std::unique_ptr<ElementRendererData>> m_elementRendererData;
|
||||
std::vector<ElementRenderer::RenderStates> m_renderStates;
|
||||
std::vector<RenderElementOwner> m_renderElements;
|
||||
std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances;
|
||||
RenderQueue<const RenderElement*> m_renderQueue;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ namespace Nz
|
|||
DirectionalLight(DirectionalLight&&) noexcept = 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Graphics/RenderElementPool.hpp>
|
||||
#include <Nazara/Renderer/RenderBufferView.hpp>
|
||||
#include <NazaraUtils/SparsePtr.hpp>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
|
@ -24,6 +25,7 @@ namespace Nz
|
|||
class CommandBufferBuilder;
|
||||
class RenderElement;
|
||||
class RenderFrame;
|
||||
class Texture;
|
||||
class ViewerInstance;
|
||||
struct ElementRendererData;
|
||||
|
||||
|
|
@ -38,7 +40,7 @@ namespace Nz
|
|||
virtual RenderElementPoolBase& GetPool() = 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 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);
|
||||
|
|
@ -47,12 +49,14 @@ namespace Nz
|
|||
{
|
||||
RenderStates()
|
||||
{
|
||||
shadowMaps2D.fill(nullptr);
|
||||
shadowMapsCube.fill(nullptr);
|
||||
shadowMapsDirectional.fill(nullptr);
|
||||
shadowMapsPoint.fill(nullptr);
|
||||
shadowMapsSpot.fill(nullptr);
|
||||
}
|
||||
|
||||
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMaps2D;
|
||||
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsCube;
|
||||
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsDirectional;
|
||||
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsPoint;
|
||||
std::array<const Texture*, PredefinedLightData::MaxLightCount> shadowMapsSpot;
|
||||
RenderBufferView lightData;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -125,8 +125,9 @@ namespace Nz
|
|||
InstanceDataUbo,
|
||||
LightDataUbo,
|
||||
OverlayTexture,
|
||||
Shadowmap2D,
|
||||
ShadowmapCube,
|
||||
ShadowmapDirectional,
|
||||
ShadowmapPoint,
|
||||
ShadowmapSpot,
|
||||
SkeletalDataUbo,
|
||||
ViewerDataUbo,
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/DebugDrawPipelinePass.hpp>
|
||||
#include <Nazara/Graphics/DepthPipelinePass.hpp>
|
||||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/ForwardPipelinePass.hpp>
|
||||
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
|
|
@ -35,6 +34,7 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
class LightShadowData;
|
||||
class RenderFrame;
|
||||
class RenderTarget;
|
||||
|
||||
|
|
@ -59,7 +59,8 @@ namespace Nz
|
|||
std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) 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;
|
||||
|
||||
|
|
@ -132,6 +133,12 @@ namespace Nz
|
|||
|
||||
struct ViewerData
|
||||
{
|
||||
struct FrameData
|
||||
{
|
||||
Bitset<UInt64> visibleLights;
|
||||
Frustumf frustum;
|
||||
};
|
||||
|
||||
std::size_t finalColorAttachment;
|
||||
std::size_t forwardColorAttachment;
|
||||
std::size_t debugColorAttachment;
|
||||
|
|
@ -145,6 +152,8 @@ namespace Nz
|
|||
RenderQueueRegistry forwardRegistry;
|
||||
RenderQueue<RenderElement*> forwardRenderQueue;
|
||||
ShaderBindingPtr blitShaderBinding;
|
||||
FrameData frame;
|
||||
bool pendingDestruction = false;
|
||||
|
||||
NazaraSlot(TransferInterface, OnTransferRequired, onTransferRequired);
|
||||
};
|
||||
|
|
@ -158,17 +167,17 @@ namespace Nz
|
|||
|
||||
std::unordered_map<const RenderTarget*, RenderTargetData> m_renderTargets;
|
||||
std::unordered_map<MaterialInstance*, MaterialInstanceData> m_materialInstances;
|
||||
std::vector<ElementRenderer::RenderStates> m_renderStates;
|
||||
mutable std::vector<FramePipelinePass::VisibleRenderable> m_visibleRenderables;
|
||||
std::vector<std::size_t> m_visibleLights;
|
||||
robin_hood::unordered_set<TransferInterface*> m_transferSet;
|
||||
BakedFrameGraph m_bakedFrameGraph;
|
||||
Bitset<UInt64> m_shadowCastingLights;
|
||||
Bitset<UInt64> m_activeLights;
|
||||
Bitset<UInt64> m_removedSkeletonInstances;
|
||||
Bitset<UInt64> m_removedViewerInstances;
|
||||
Bitset<UInt64> m_removedWorldInstances;
|
||||
Bitset<UInt64> m_shadowCastingLights;
|
||||
Bitset<UInt64> m_visibleShadowCastingLights;
|
||||
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<SkeletonInstanceData> m_skeletonInstances;
|
||||
MemoryPool<ViewerData> m_viewerPool;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@
|
|||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/FramePipelinePass.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/MaterialInstance.hpp>
|
||||
#include <Nazara/Graphics/MaterialPass.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/RenderElementOwner.hpp>
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
|
|
@ -24,13 +22,16 @@
|
|||
namespace Nz
|
||||
{
|
||||
class AbstractViewer;
|
||||
class DirectionalLight;
|
||||
class ElementRendererRegistry;
|
||||
class FrameGraph;
|
||||
class FramePass;
|
||||
class FramePipeline;
|
||||
class Light;
|
||||
|
||||
class NAZARA_GRAPHICS_API ForwardPipelinePass : public FramePipelinePass
|
||||
class PointLight;
|
||||
class SpotLight;
|
||||
|
||||
class NAZARA_GRAPHICS_API ForwardPipelinePass : public FramePipelinePass, TransferInterface
|
||||
{
|
||||
public:
|
||||
ForwardPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer);
|
||||
|
|
@ -41,7 +42,7 @@ namespace Nz
|
|||
inline void InvalidateCommandBuffers();
|
||||
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);
|
||||
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=(ForwardPipelinePass&&) = delete;
|
||||
|
||||
static constexpr std::size_t MaxLightCountPerDraw = 3;
|
||||
|
||||
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
|
||||
{
|
||||
std::size_t usedCount = 1;
|
||||
|
|
@ -62,55 +68,30 @@ namespace Nz
|
|||
NazaraSlot(MaterialInstance, OnMaterialInstanceShaderBindingInvalidated, onMaterialInstanceShaderBindingInvalidated);
|
||||
};
|
||||
|
||||
using LightKey = std::array<const Light*, MaxLightCountPerDraw>;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct RenderableLight
|
||||
{
|
||||
const Light* light;
|
||||
const T* light;
|
||||
std::size_t lightIndex;
|
||||
float contributionScore;
|
||||
};
|
||||
|
||||
std::size_t m_forwardPassIndex;
|
||||
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<ElementRenderer::RenderStates> m_renderStates;
|
||||
std::vector<RenderElementOwner> m_renderElements;
|
||||
std::unordered_map<const MaterialInstance*, MaterialPassEntry> m_materialInstances;
|
||||
std::unordered_map<const RenderElement*, LightPerElementData> m_lightPerRenderElement;
|
||||
std::unordered_map<LightKey, RenderBufferView, LightKeyHasher> m_lightBufferPerLights;
|
||||
std::vector<LightDataUbo> m_lightDataBuffers;
|
||||
std::vector<RenderableLight> m_renderableLights;
|
||||
std::vector<RenderableLight<DirectionalLight>> m_directionalLights;
|
||||
std::vector<RenderableLight<PointLight>> m_pointLights;
|
||||
std::vector<RenderableLight<SpotLight>> m_spotLights;
|
||||
ElementRenderer::RenderStates m_renderState;
|
||||
RenderQueue<const RenderElement*> m_renderQueue;
|
||||
RenderQueueRegistry m_renderQueueRegistry;
|
||||
AbstractViewer* m_viewer;
|
||||
ElementRendererRegistry& m_elementRegistry;
|
||||
FramePipeline& m_pipeline;
|
||||
UploadPool::Allocation* m_pendingLightUploadAllocation;
|
||||
bool m_rebuildCommandBuffer;
|
||||
bool m_rebuildElements;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,21 +15,6 @@ namespace Nz
|
|||
{
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/LightShadowData.hpp>
|
||||
#include <Nazara/Math/BoundingVolume.hpp>
|
||||
#include <Nazara/Math/Frustum.hpp>
|
||||
#include <Nazara/Math/Quaternion.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Utility/PixelFormat.hpp>
|
||||
|
|
@ -19,12 +20,8 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
class CommandBufferBuilder;
|
||||
class ElementRendererRegistry;
|
||||
class FramePipeline;
|
||||
class RenderBuffer;
|
||||
class RenderFrame;
|
||||
class Texture;
|
||||
|
||||
class NAZARA_GRAPHICS_API Light
|
||||
{
|
||||
|
|
@ -34,11 +31,11 @@ namespace Nz
|
|||
Light(Light&&) noexcept = default;
|
||||
virtual ~Light();
|
||||
|
||||
virtual float ComputeContributionScore(const BoundingVolumef& boundingVolume) const = 0;
|
||||
virtual float ComputeContributionScore(const Frustumf& viewerFrustum) const = 0;
|
||||
|
||||
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 UInt8 GetLightType() const;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
class AbstractViewer;
|
||||
class BakedFrameGraph;
|
||||
class FrameGraph;
|
||||
class FramePass;
|
||||
|
|
@ -22,25 +23,33 @@ namespace Nz
|
|||
class NAZARA_GRAPHICS_API LightShadowData
|
||||
{
|
||||
public:
|
||||
LightShadowData() = default;
|
||||
inline LightShadowData();
|
||||
LightShadowData(const LightShadowData&) = delete;
|
||||
LightShadowData(LightShadowData&&) = delete;
|
||||
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 RegisterPassInputs(FramePass& pass) = 0;
|
||||
virtual void RegisterToFrameGraph(FrameGraph& frameGraph) = 0;
|
||||
virtual void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) = 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 UnregisterViewer(const AbstractViewer* viewer);
|
||||
|
||||
LightShadowData& operator=(const LightShadowData&) = delete;
|
||||
LightShadowData& operator=(LightShadowData&&) = delete;
|
||||
|
||||
protected:
|
||||
inline void UpdatePerViewerStatus(bool isPerViewer);
|
||||
|
||||
private:
|
||||
bool m_isPerViewer;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,20 @@
|
|||
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ namespace Nz
|
|||
PointLight(PointLight&&) noexcept = 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;
|
||||
|
||||
|
|
@ -33,6 +33,7 @@ namespace Nz
|
|||
inline float GetDiffuseFactor() const;
|
||||
inline Color GetColor() const;
|
||||
inline const Vector3f& GetPosition() const;
|
||||
inline float GetInvRadius() const;
|
||||
inline float GetRadius() const;
|
||||
|
||||
inline void UpdateAmbientFactor(float factor);
|
||||
|
|
|
|||
|
|
@ -28,14 +28,19 @@ namespace Nz
|
|||
return m_color;
|
||||
}
|
||||
|
||||
inline float PointLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline const Vector3f& PointLight::GetPosition() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
inline float PointLight::GetDiffuseFactor() const
|
||||
inline float PointLight::GetInvRadius() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
return m_invRadius;
|
||||
}
|
||||
|
||||
inline float PointLight::GetRadius() const
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ namespace Nz
|
|||
PointLightShadowData(PointLightShadowData&&) = delete;
|
||||
~PointLightShadowData() = default;
|
||||
|
||||
void PrepareRendering(RenderFrame& renderFrame) override;
|
||||
void PrepareRendering(RenderFrame& renderFrame, const AbstractViewer* viewer) override;
|
||||
|
||||
void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
|
||||
void RegisterPassInputs(FramePass& pass) override;
|
||||
void RegisterToFrameGraph(FrameGraph& frameGraph) override;
|
||||
void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,19 +24,24 @@ namespace Nz
|
|||
SpotLight(SpotLight&&) noexcept = 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 GetDiffuseFactor() const;
|
||||
inline Color GetColor() const;
|
||||
inline const Vector3f& GetDirection() const;
|
||||
inline RadianAnglef GetInnerAngle() const;
|
||||
inline float GetInnerAngleCos() const;
|
||||
inline float GetInvRadius() const;
|
||||
inline RadianAnglef GetOuterAngle() const;
|
||||
inline float GetOuterAngleCos() const;
|
||||
inline float GetOuterAngleTan() const;
|
||||
inline const Vector3f& GetPosition() const;
|
||||
inline const Quaternionf& GetRotation() const;
|
||||
inline float GetRadius() const;
|
||||
inline const Matrix4f& GetViewProjMatrix() const;
|
||||
|
||||
std::unique_ptr<LightShadowData> InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ namespace Nz
|
|||
return m_color;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline const Vector3f& SpotLight::GetDirection() const
|
||||
{
|
||||
return m_direction;
|
||||
|
|
@ -41,11 +46,31 @@ namespace Nz
|
|||
return m_innerAngle;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetInnerAngleCos() const
|
||||
{
|
||||
return m_innerAngleCos;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetInvRadius() const
|
||||
{
|
||||
return m_invRadius;
|
||||
}
|
||||
|
||||
inline RadianAnglef SpotLight::GetOuterAngle() const
|
||||
{
|
||||
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
|
||||
{
|
||||
return m_position;
|
||||
|
|
@ -56,16 +81,16 @@ namespace Nz
|
|||
return m_rotation;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetDiffuseFactor() const
|
||||
{
|
||||
return m_diffuseFactor;
|
||||
}
|
||||
|
||||
inline float SpotLight::GetRadius() const
|
||||
{
|
||||
return m_radius;
|
||||
}
|
||||
|
||||
inline const Matrix4f& SpotLight::GetViewProjMatrix() const
|
||||
{
|
||||
return m_viewProjMatrix;
|
||||
}
|
||||
|
||||
inline void SpotLight::UpdateAmbientFactor(float factor)
|
||||
{
|
||||
m_ambientFactor = factor;
|
||||
|
|
@ -164,10 +189,10 @@ namespace Nz
|
|||
|
||||
inline void SpotLight::UpdateViewProjMatrix()
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
Matrix4f projection = Matrix4f::Perspective(m_outerAngle * 2.f, 1.f, 0.01f, m_radius);
|
||||
Matrix4f view = Matrix4f::TransformInverse(m_position, m_rotation);
|
||||
|
|
|
|||
|
|
@ -26,13 +26,15 @@ namespace Nz
|
|||
SpotLightShadowData(SpotLightShadowData&&) = delete;
|
||||
~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 RegisterPassInputs(FramePass& pass) override;
|
||||
void RegisterToFrameGraph(FrameGraph& frameGraph) override;
|
||||
void RegisterPassInputs(FramePass& pass, const AbstractViewer* viewer) 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
inline const ViewerInstance& SpotLightShadowData::GetViewerInstance() const
|
||||
{
|
||||
return m_viewer.GetViewerInstance();
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace Nz
|
|||
RenderElementPool<RenderSpriteChain>& GetPool() 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 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;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Nz
|
|||
RenderElementPool<RenderSubmesh>& GetPool() 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 Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ namespace Nz
|
|||
constexpr IntersectionSide Intersect(const Sphere<T>& sphere) 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;
|
||||
|
||||
constexpr Frustum& operator=(const Frustum&) = default;
|
||||
|
|
|
|||
|
|
@ -427,6 +427,38 @@ namespace Nz
|
|||
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
|
||||
* \return A string representation of the object: "Frustum(Plane ...)"
|
||||
|
|
|
|||
|
|
@ -83,14 +83,13 @@ namespace Nz
|
|||
|
||||
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)
|
||||
{
|
||||
ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType);
|
||||
|
||||
m_renderStates.clear();
|
||||
m_renderStates.resize(elementCount);
|
||||
|
||||
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data());
|
||||
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, SparsePtr(&defaultRenderStates, 0));
|
||||
});
|
||||
|
||||
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)
|
||||
|
|
|
|||
|
|
@ -3,36 +3,24 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <Nazara/Graphics/Enums.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/DirectionalLightShadowData.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float DirectionalLight::ComputeContributionScore(const BoundingVolumef& /*boundingVolume*/) const
|
||||
float DirectionalLight::ComputeContributionScore(const Frustumf& /*viewerFrustum*/) const
|
||||
{
|
||||
return -std::numeric_limits<float>::infinity();
|
||||
}
|
||||
|
||||
void DirectionalLight::FillLightData(void* data) const
|
||||
bool DirectionalLight::FrustumCull(const Frustumf& /*viewerFrustum*/) const
|
||||
{
|
||||
auto lightOffset = PredefinedLightData::GetOffsets();
|
||||
|
||||
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);
|
||||
return true; //< always visible
|
||||
}
|
||||
|
||||
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*/)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ namespace Nz
|
|||
{
|
||||
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*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ namespace Nz
|
|||
//TODO: Switch lights to storage buffers so they can all be part of GPU memory
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
|
||||
|
||||
if (viewerRenderMask & renderMask)
|
||||
|
|
@ -125,6 +128,17 @@ namespace Nz
|
|||
{
|
||||
m_shadowCastingLights.UnboundedSet(lightIndex);
|
||||
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
|
||||
{
|
||||
|
|
@ -139,6 +153,18 @@ namespace Nz
|
|||
{
|
||||
m_shadowCastingLights.UnboundedSet(lightIndex);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +187,9 @@ namespace Nz
|
|||
// TODO: Invalidate only relevant viewers and passes
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
|
||||
|
||||
if (viewerRenderMask & renderMask)
|
||||
|
|
@ -181,6 +210,9 @@ namespace Nz
|
|||
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (viewerData.depthPrepass)
|
||||
viewerData.depthPrepass->RegisterMaterialInstance(*newMaterial);
|
||||
|
||||
|
|
@ -195,6 +227,9 @@ namespace Nz
|
|||
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (viewerData.depthPrepass)
|
||||
viewerData.depthPrepass->UnregisterMaterialInstance(*prevMaterial);
|
||||
|
||||
|
|
@ -212,6 +247,9 @@ namespace Nz
|
|||
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (viewerData.depthPrepass)
|
||||
viewerData.depthPrepass->RegisterMaterialInstance(*mat);
|
||||
|
||||
|
|
@ -262,6 +300,14 @@ namespace Nz
|
|||
|
||||
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;
|
||||
|
||||
return viewerIndex;
|
||||
|
|
@ -287,12 +333,21 @@ namespace Nz
|
|||
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))
|
||||
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)
|
||||
|
|
@ -334,75 +389,67 @@ namespace Nz
|
|||
else
|
||||
frameGraphInvalidated = m_bakedFrameGraph.Resize(renderFrame);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
// Find active lights (i.e. visible in any frustum)
|
||||
m_activeLights.Clear();
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 renderMask = viewerData.viewer->GetRenderMask();
|
||||
|
||||
// Frustum culling
|
||||
// Extract frustum from viewproj matrix
|
||||
const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix();
|
||||
viewerData.frame.frustum = Frustumf::Extract(viewProjMatrix);
|
||||
|
||||
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
|
||||
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();
|
||||
viewerData.frame.visibleLights.Clear();
|
||||
for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it)
|
||||
{
|
||||
const LightData& lightData = *it;
|
||||
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)
|
||||
if (renderMask & lightData.renderMask && frustum.Intersect(boundingVolume) != IntersectionSide::Outside)
|
||||
{
|
||||
m_visibleLights.push_back(lightIndex);
|
||||
m_activeLights.UnboundedSet(lightIndex);
|
||||
viewerData.frame.visibleLights.UnboundedSet(lightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
|
||||
{
|
||||
return currentHash * 23 + newHash;
|
||||
};
|
||||
m_visibleShadowCastingLights.PerformsAND(m_activeLights, m_shadowCastingLights);
|
||||
|
||||
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)
|
||||
viewerData.depthPrepass->Prepare(renderFrame, frustum, visibleRenderables, depthVisibilityHash);
|
||||
// Frustum culling
|
||||
std::size_t visibilityHash = 5;
|
||||
const auto& visibleRenderables = FrustumCull(viewerData.frame.frustum, renderMask, visibilityHash);
|
||||
|
||||
if (viewerData.gammaCorrectionPass)
|
||||
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)
|
||||
viewerData.debugDrawPass->Prepare(renderFrame);
|
||||
|
|
@ -413,6 +460,9 @@ namespace Nz
|
|||
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (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_rebuildFrameGraph = false;
|
||||
|
||||
|
|
@ -494,7 +562,11 @@ namespace Nz
|
|||
void ForwardFramePipeline::UnregisterLight(std::size_t 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)
|
||||
|
|
@ -509,6 +581,9 @@ namespace Nz
|
|||
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (viewerData.depthPrepass)
|
||||
viewerData.depthPrepass->UnregisterMaterialInstance(*material);
|
||||
|
||||
|
|
@ -521,20 +596,31 @@ namespace Nz
|
|||
|
||||
void ForwardFramePipeline::UnregisterSkeleton(std::size_t skeletonIndex)
|
||||
{
|
||||
// Defer world instance release
|
||||
// Defer instance release
|
||||
m_removedSkeletonInstances.UnboundedSet(skeletonIndex);
|
||||
}
|
||||
|
||||
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_rebuildFrameGraph = true;
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::UnregisterWorldInstance(std::size_t worldInstance)
|
||||
{
|
||||
// Defer world instance release
|
||||
// Defer instance release
|
||||
m_removedWorldInstances.UnboundedSet(worldInstance);
|
||||
}
|
||||
|
||||
|
|
@ -558,6 +644,9 @@ namespace Nz
|
|||
// TODO: Invalidate only relevant viewers and passes
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
|
||||
|
||||
if (viewerRenderMask & renderableData->renderMask)
|
||||
|
|
@ -578,6 +667,9 @@ namespace Nz
|
|||
// TODO: Invalidate only relevant viewers and passes
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 viewerRenderMask = viewerData.viewer->GetRenderMask();
|
||||
|
||||
if (viewerRenderMask & renderableData->renderMask)
|
||||
|
|
@ -593,6 +685,7 @@ namespace Nz
|
|||
void ForwardFramePipeline::UpdateViewerRenderMask(std::size_t viewerIndex, Int32 renderOrder)
|
||||
{
|
||||
ViewerData* viewerData = m_viewerPool.RetrieveFromIndex(viewerIndex);
|
||||
assert(!viewerData->pendingDestruction);
|
||||
if (viewerData->renderOrder != renderOrder)
|
||||
{
|
||||
viewerData->renderOrder = renderOrder;
|
||||
|
|
@ -607,11 +700,23 @@ namespace Nz
|
|||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
{
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
lightData->shadowData->RegisterToFrameGraph(frameGraph);
|
||||
if (!lightData->shadowData->IsPerViewer())
|
||||
lightData->shadowData->RegisterToFrameGraph(frameGraph, nullptr);
|
||||
}
|
||||
|
||||
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({
|
||||
"Forward output",
|
||||
PixelFormat::RGBA8
|
||||
|
|
@ -629,7 +734,8 @@ namespace Nz
|
|||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
{
|
||||
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;
|
||||
|
|
@ -660,6 +766,9 @@ namespace Nz
|
|||
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget();
|
||||
*viewerIt++ = std::make_pair(&renderTarget, &viewerData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,18 @@
|
|||
|
||||
#include <Nazara/Graphics/ForwardPipelinePass.hpp>
|
||||
#include <Nazara/Graphics/AbstractViewer.hpp>
|
||||
#include <Nazara/Graphics/DirectionalLight.hpp>
|
||||
#include <Nazara/Graphics/ElementRendererRegistry.hpp>
|
||||
#include <Nazara/Graphics/FrameGraph.hpp>
|
||||
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <Nazara/Graphics/SpotLight.hpp>
|
||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||
#include <Nazara/Graphics/DirectionalLightShadowData.hpp>
|
||||
#include <Nazara/Graphics/SpotLightShadowData.hpp>
|
||||
#include <Nazara/Graphics/ViewerInstance.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Renderer/RenderFrame.hpp>
|
||||
|
|
@ -23,155 +28,41 @@ namespace Nz
|
|||
m_viewer(viewer),
|
||||
m_elementRegistry(elementRegistry),
|
||||
m_pipeline(owner),
|
||||
m_pendingLightUploadAllocation(nullptr),
|
||||
m_rebuildCommandBuffer(false),
|
||||
m_rebuildElements(false)
|
||||
{
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
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
|
||||
{
|
||||
renderFrame.PushForRelease(std::move(m_renderElements));
|
||||
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)
|
||||
{
|
||||
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{
|
||||
&renderableData.scissorBox,
|
||||
renderableData.skeletonInstance,
|
||||
renderableData.worldInstance
|
||||
};
|
||||
|
||||
std::size_t previousCount = m_renderElements.size();
|
||||
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)
|
||||
{
|
||||
renderElement->Register(m_renderQueueRegistry);
|
||||
|
|
@ -180,25 +71,8 @@ namespace Nz
|
|||
|
||||
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_rebuildElements = true;
|
||||
InvalidateElements();
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
PrepareLights(renderFrame, frustum, visibleLights);
|
||||
|
||||
if (m_rebuildElements)
|
||||
{
|
||||
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)
|
||||
|
|
@ -222,41 +98,10 @@ namespace Nz
|
|||
|
||||
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)
|
||||
{
|
||||
ElementRenderer& elementRenderer = m_elementRegistry.GetElementRenderer(elementType);
|
||||
|
||||
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());
|
||||
elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, SparsePtr(&m_renderState, 0));
|
||||
});
|
||||
|
||||
m_elementRegistry.ForEachElementRenderer([&](std::size_t elementType, ElementRenderer& elementRenderer)
|
||||
|
|
@ -342,4 +187,165 @@ namespace Nz
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ namespace Nz
|
|||
{
|
||||
std::size_t depthPassIndex = m_materialPassRegistry.GetPassIndex("DepthPass");
|
||||
std::size_t shadowPassIndex = m_materialPassRegistry.GetPassIndex("ShadowPass");
|
||||
std::size_t distanceShadowPassIndex = m_materialPassRegistry.GetPassIndex("DistanceShadowPass");
|
||||
std::size_t forwardPassIndex = m_materialPassRegistry.GetPassIndex("ForwardPass");
|
||||
|
||||
// BasicMaterial
|
||||
|
|
@ -257,9 +258,12 @@ namespace Nz
|
|||
settings.AddPass(depthPassIndex, depthPass);
|
||||
|
||||
MaterialPass shadowPass = depthPass;
|
||||
shadowPass.states.faceCulling = FaceCulling::Front;
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -279,9 +283,12 @@ namespace Nz
|
|||
settings.AddPass(depthPassIndex, depthPass);
|
||||
|
||||
MaterialPass shadowPass = depthPass;
|
||||
shadowPass.states.faceCulling = FaceCulling::Front;
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -301,12 +308,12 @@ namespace Nz
|
|||
settings.AddPass(depthPassIndex, 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);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -399,6 +406,7 @@ namespace Nz
|
|||
m_materialPassRegistry.RegisterPass("ForwardPass");
|
||||
m_materialPassRegistry.RegisterPass("DepthPass");
|
||||
m_materialPassRegistry.RegisterPass("ShadowPass");
|
||||
m_materialPassRegistry.RegisterPass("DistanceShadowPass");
|
||||
}
|
||||
|
||||
void Graphics::RegisterShaderModules()
|
||||
|
|
|
|||
|
|
@ -8,4 +8,12 @@
|
|||
namespace Nz
|
||||
{
|
||||
LightShadowData::~LightShadowData() = default;
|
||||
|
||||
void LightShadowData::RegisterViewer(const AbstractViewer* /*viewer*/)
|
||||
{
|
||||
}
|
||||
|
||||
void LightShadowData::UnregisterViewer(const AbstractViewer* /*viewer*/)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ namespace Nz
|
|||
options.partialSanitization = true;
|
||||
options.moduleResolver = graphics->GetShaderModuleResolver();
|
||||
options.optionValues[CRC32("MaxLightCount")] = SafeCast<UInt32>(PredefinedLightData::MaxLightCount);
|
||||
options.optionValues[CRC32("MaxLightCascadeCount")] = SafeCast<UInt32>(PredefinedDirectionalLightData::MaxLightCascadeCount);
|
||||
options.optionValues[CRC32("MaxJointCount")] = SafeCast<UInt32>(PredefinedSkeletalData::MaxMatricesCount);
|
||||
|
||||
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())
|
||||
m_engineShaderBindings[EngineShaderBinding::ViewerDataUbo] = it->second.bindingIndex;
|
||||
|
||||
if (auto it = block->samplers.find("ShadowMaps2D"); it != block->samplers.end())
|
||||
m_engineShaderBindings[EngineShaderBinding::Shadowmap2D] = it->second.bindingIndex;
|
||||
if (auto it = block->samplers.find("ShadowMapsDirectional"); it != block->samplers.end())
|
||||
m_engineShaderBindings[EngineShaderBinding::ShadowmapDirectional] = it->second.bindingIndex;
|
||||
|
||||
if (auto it = block->samplers.find("ShadowMapsCube"); it != block->samplers.end())
|
||||
m_engineShaderBindings[EngineShaderBinding::ShadowmapCube] = it->second.bindingIndex;
|
||||
if (auto it = block->samplers.find("ShadowMapsPoint"); it != block->samplers.end())
|
||||
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())
|
||||
m_engineShaderBindings[EngineShaderBinding::SkeletalDataUbo] = it->second.bindingIndex;
|
||||
|
|
|
|||
|
|
@ -3,33 +3,20 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/PointLight.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <Nazara/Graphics/Graphics.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>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float PointLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const
|
||||
float PointLight::ComputeContributionScore(const Frustumf& viewerFrustum) const
|
||||
{
|
||||
// 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();
|
||||
|
||||
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);
|
||||
return viewerFrustum.Intersect(Spheref(m_position, m_radius)) != IntersectionSide::Outside;
|
||||
}
|
||||
|
||||
std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
const Matrix4f& viewProjMatrix = direction.viewer.GetViewerInstance().GetViewProjMatrix();
|
||||
|
|
@ -114,8 +116,10 @@ namespace Nz
|
|||
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);
|
||||
pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput);
|
||||
|
||||
|
|
@ -123,8 +127,10 @@ namespace Nz
|
|||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,61 @@
|
|||
[nzsl_version("1.0")]
|
||||
module Engine.LightData;
|
||||
|
||||
option MaxLightCascadeCount: u32 = u32(4); //< FIXME: Fix integral value types
|
||||
option MaxLightCount: u32 = u32(3); //< FIXME: Fix integral value types
|
||||
|
||||
[export]
|
||||
[layout(std140)]
|
||||
struct Light
|
||||
struct DirectionalLight
|
||||
{
|
||||
type: i32,
|
||||
color: vec4[f32],
|
||||
factor: vec2[f32],
|
||||
parameter1: vec4[f32],
|
||||
parameter2: vec4[f32],
|
||||
parameter3: vec4[f32],
|
||||
invShadowMapSize: vec2[f32],
|
||||
viewProjMatrix: mat4[f32]
|
||||
color: vec3[f32],
|
||||
direction: vec3[f32],
|
||||
invShadowMapSize: vec2[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]
|
||||
[layout(std140)]
|
||||
struct LightData
|
||||
{
|
||||
lights: array[Light, MaxLightCount],
|
||||
lightCount: u32,
|
||||
directionalLights: array[DirectionalLight, MaxLightCount],
|
||||
pointLights: array[PointLight, MaxLightCount],
|
||||
spotLights: array[SpotLight, MaxLightCount],
|
||||
|
||||
directionalLightCount: u32,
|
||||
pointLightCount: u32,
|
||||
spotLightCount: u32,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear;
|
|||
|
||||
// Pass-specific options
|
||||
option DepthPass: bool = false;
|
||||
option DistanceDepth: bool = false;
|
||||
|
||||
// Basic material options
|
||||
option HasBaseColorTexture: bool = false;
|
||||
|
|
@ -39,6 +40,7 @@ option VertexUvLoc: i32 = -1;
|
|||
option VertexJointIndicesLoc: 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
|
||||
|
||||
const HasNormal = (VertexNormalLoc >= 0);
|
||||
|
|
@ -98,8 +100,9 @@ external
|
|||
[tag("ViewerData")] viewerData: uniform[ViewerData],
|
||||
[tag("SkeletalData")] skeletalData: uniform[SkeletalData],
|
||||
[tag("LightData")] lightData: uniform[LightData],
|
||||
[tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount],
|
||||
[tag("ShadowMapsCube")] shadowMapsCube: array[sampler_cube[f32], MaxLightCount]
|
||||
[tag("ShadowMapsDirectional")] shadowMapsDirectional: array[depth_sampler2D_array[f32], MaxLightCount],
|
||||
[tag("ShadowMapsPoint")] shadowMapsPoint: array[sampler_cube[f32], MaxLightCount],
|
||||
[tag("ShadowMapsSpot")] shadowMapsSpot: array[depth_sampler2D[f32], MaxLightCount],
|
||||
}
|
||||
|
||||
struct VertToFrag
|
||||
|
|
@ -109,14 +112,15 @@ struct VertToFrag
|
|||
[location(2), cond(HasColor)] color: vec4[f32],
|
||||
[location(3), cond(HasNormal)] normal: vec3[f32],
|
||||
[location(4), cond(HasNormalMapping)] tangent: vec3[f32],
|
||||
[location(5), cond(HasLighting)] lightProjPos: array[vec4[f32], MaxLightCount],
|
||||
[builtin(position)] position: vec4[f32],
|
||||
[builtin(frag_coord)] fragcoord: vec4[f32]
|
||||
}
|
||||
|
||||
// Fragment stage
|
||||
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
|
||||
|
|
@ -124,8 +128,7 @@ fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32
|
|||
return zNear * zFar / (zFar + depth * (zNear - zFar));
|
||||
}
|
||||
|
||||
[entry(frag), cond(!DepthPass || AlphaTest)]
|
||||
fn main(input: VertToFrag) -> FragOut
|
||||
fn ComputeColor(input: VertToFrag) -> vec4[f32]
|
||||
{
|
||||
let color = settings.BaseColor;
|
||||
|
||||
|
|
@ -141,6 +144,13 @@ fn main(input: VertToFrag) -> FragOut
|
|||
const if (HasAlphaTexture)
|
||||
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)
|
||||
{
|
||||
if (color.w < settings.AlphaThreshold)
|
||||
|
|
@ -168,139 +178,177 @@ fn main(input: VertToFrag) -> FragOut
|
|||
else
|
||||
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 lightDiffuseFactor = light.factor.y;
|
||||
let lambert = max(dot(normal, -light.direction), 0.0);
|
||||
|
||||
// TODO: Add switch instruction
|
||||
if (light.type == DirectionalLight)
|
||||
let reflection = reflect(light.direction, normal);
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
const offset = 0.005;
|
||||
let lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](input.worldPos, 1.0);
|
||||
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]
|
||||
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]
|
||||
for y in 0 -> sampleCount
|
||||
for z in 0 -> sampleCount
|
||||
{
|
||||
[unroll]
|
||||
for z in 0 -> sampleCount
|
||||
{
|
||||
let dirOffset = vec3[f32](f32(x), f32(y), f32(z)) * invSampleCount * offset - start;
|
||||
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;
|
||||
depth = LinearizeDepth(depth, 0.01, lightRadius);
|
||||
|
||||
if (depth > dist)
|
||||
shadowFactor += shadowContribution;
|
||||
}
|
||||
let closestDepth = shadowMapsPoint[lightIndex].Sample(sampleDir).r;
|
||||
closestDepth *= light.radius;
|
||||
|
||||
if (closestDepth > dist - bias)
|
||||
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;
|
||||
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)
|
||||
{
|
||||
if (light.invShadowMapSize.x > 0.0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
let bias = 0.0005 * tan(acos(lambert));
|
||||
bias = clamp(bias, 0.0, 0.01);
|
||||
|
||||
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
|
||||
lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor;
|
||||
lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb;
|
||||
let lightProjPos = light.viewProjMatrix * vec4[f32](input.worldPos, 1.0);
|
||||
let shadowCoords = lightProjPos.xyz / lightProjPos.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 += 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;
|
||||
|
|
@ -310,22 +358,50 @@ fn main(input: VertToFrag) -> FragOut
|
|||
|
||||
let lightColor = lightAmbient + lightDiffuse + lightSpecular;
|
||||
|
||||
let output: FragOut;
|
||||
output.RenderTarget0 = vec4[f32](lightColor, 1.0) * color;
|
||||
return output;
|
||||
}
|
||||
else
|
||||
{
|
||||
let output: FragOut;
|
||||
output.RenderTarget0 = color;
|
||||
return output;
|
||||
color.rgb *= lightColor;
|
||||
}
|
||||
|
||||
let output: FragOut;
|
||||
output.RenderTarget0 = color;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Dummy fragment shader (TODO: Add a way to delete stage?)
|
||||
[entry(frag), cond(DepthPass && !AlphaTest)]
|
||||
fn main() {}
|
||||
// Shadow passes entries
|
||||
[entry(frag), cond(DepthPass && DistanceDepth)]
|
||||
[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
|
||||
struct VertIn
|
||||
|
|
@ -432,7 +508,7 @@ fn main(input: VertIn) -> VertToFrag
|
|||
output.worldPos = worldPosition.xyz;
|
||||
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)
|
||||
output.color = input.color;
|
||||
|
|
@ -446,11 +522,5 @@ fn main(input: VertIn) -> VertToFrag
|
|||
const if (HasNormalMapping)
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ fn main(input: VertToFrag) -> FragOut
|
|||
|
||||
let albedoFactor = albedo / Pi;
|
||||
|
||||
for i in u32(0) -> lightData.lightCount
|
||||
/*for i in u32(0) -> lightData.lightCount
|
||||
{
|
||||
let light = lightData.lights[i];
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ fn main(input: VertToFrag) -> FragOut
|
|||
|
||||
let NdotL = max(dot(normal, lightToPosNorm), 0.0);
|
||||
lightRadiance += (diffuse * albedoFactor + specular) * radiance * NdotL;
|
||||
}
|
||||
}*/
|
||||
|
||||
let ambient = (0.03).rrr * albedo;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,35 +3,37 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.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/Math/Vector2.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
float SpotLight::ComputeContributionScore(const BoundingVolumef& boundingVolume) const
|
||||
float SpotLight::ComputeContributionScore(const Frustumf& viewerFrustum) const
|
||||
{
|
||||
// TODO: take luminosity/radius/direction into account
|
||||
return Vector3f::SquaredDistance(m_position, boundingVolume.aabb.GetCenter());
|
||||
// TODO: take luminosity/radius into account
|
||||
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);
|
||||
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, m_invRadius);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f);
|
||||
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter3) = Vector4f(m_innerAngleCos, m_outerAngleCos, 0.f, 0.f);
|
||||
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = (IsShadowCaster()) ? Vector2f(1.f / GetShadowMapSize()) : Vector2f(-1.f, -1.f);
|
||||
AccessByOffset<Matrix4f&>(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix;
|
||||
Vector3f base = Vector3f::Forward() * m_radius;
|
||||
Vector3f lExtend = Vector3f::Left() * opposite;
|
||||
Vector3f uExtend = Vector3f::Up() * opposite;
|
||||
|
||||
// Test five points against frustum
|
||||
std::array<Vector3f, 5> points = {
|
||||
m_position,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/SpotLight.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
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();
|
||||
|
||||
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
|
||||
|
|
@ -71,13 +74,17 @@ namespace Nz
|
|||
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);
|
||||
}
|
||||
|
||||
void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph)
|
||||
void SpotLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph, [[maybe_unused]] const AbstractViewer* viewer)
|
||||
{
|
||||
assert(viewer == nullptr);
|
||||
|
||||
UInt32 shadowMapSize = m_light.GetShadowMapSize();
|
||||
|
||||
m_attachmentIndex = frameGraph.AddAttachment({
|
||||
|
|
@ -90,7 +97,7 @@ namespace Nz
|
|||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace Nz
|
|||
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();
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Nz
|
|||
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();
|
||||
|
||||
|
|
@ -55,6 +55,7 @@ namespace Nz
|
|||
};
|
||||
|
||||
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& whiteTexture2D = Graphics::Instance()->GetDefaultTextures().whiteTextures[ImageType::E2D];
|
||||
const auto& defaultSampler = graphics->GetSamplerCache().Get({});
|
||||
|
|
@ -129,7 +130,7 @@ namespace Nz
|
|||
|
||||
m_bindingCache.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);
|
||||
|
||||
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();
|
||||
|
||||
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)
|
||||
texture = depthTexture2D.get();
|
||||
texture = depthTexture2DArray.get();
|
||||
|
||||
auto& textureEntry = m_textureBindingCache.emplace_back();
|
||||
textureEntry.texture = texture;
|
||||
|
|
@ -176,17 +177,17 @@ namespace Nz
|
|||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
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();
|
||||
|
||||
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)
|
||||
texture = depthTextureCube.get();
|
||||
|
||||
|
|
@ -198,7 +199,29 @@ namespace Nz
|
|||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
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]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue