Graphics: Implement point-light shadow-mapping
This commit is contained in:
parent
6731e07b54
commit
f8238a6e6c
|
|
@ -250,17 +250,18 @@ int main()
|
||||||
|
|
||||||
{
|
{
|
||||||
auto& lightNode = registry.emplace<Nz::NodeComponent>(lightEntity3);
|
auto& lightNode = registry.emplace<Nz::NodeComponent>(lightEntity3);
|
||||||
lightNode.SetPosition(Nz::Vector3f::Backward() * 2.f);
|
//lightNode.SetPosition(Nz::Vector3f::Up() * 4.f);
|
||||||
lightNode.SetRotation(Nz::EulerAnglesf(-45.f, 180.f, 0.f));
|
lightNode.SetPosition(Nz::Vector3f::Down() * 7.5f + Nz::Vector3f::Backward() * 2.5f);
|
||||||
|
//lightNode.SetRotation(Nz::EulerAnglesf(-45.f, 180.f, 0.f));
|
||||||
lightNode.SetParentJoint(bobEntity, "Spine2");
|
lightNode.SetParentJoint(bobEntity, "Spine2");
|
||||||
|
|
||||||
auto& cameraLight = registry.emplace<Nz::LightComponent>(lightEntity3);
|
auto& cameraLight = registry.emplace<Nz::LightComponent>(lightEntity3);
|
||||||
|
|
||||||
auto& spotLight = cameraLight.AddLight<Nz::SpotLight>(0xFFFFFFFF);
|
auto& pointLight = cameraLight.AddLight<Nz::PointLight>(0xFFFFFFFF);
|
||||||
spotLight.UpdateColor(Nz::Color::Blue);
|
pointLight.UpdateColor(Nz::Color::Blue);
|
||||||
spotLight.UpdateInnerAngle(Nz::DegreeAnglef(15.f));
|
pointLight.UpdateRadius(3.f);
|
||||||
spotLight.UpdateOuterAngle(Nz::DegreeAnglef(20.f));
|
pointLight.EnableShadowCasting(true);
|
||||||
spotLight.EnableShadowCasting(true);
|
pointLight.UpdateShadowMapSize(2048);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,9 +522,9 @@ int main()
|
||||||
Nz::DebugDrawer& debugDrawer = renderSystem.GetFramePipeline().GetDebugDrawer();
|
Nz::DebugDrawer& debugDrawer = renderSystem.GetFramePipeline().GetDebugDrawer();
|
||||||
auto& lightNode = registry.get<Nz::NodeComponent>(lightEntity3);
|
auto& lightNode = registry.get<Nz::NodeComponent>(lightEntity3);
|
||||||
//debugDrawer.DrawLine(lightNode.GetPosition(Nz::CoordSys::Global), lightNode.GetForward() * 10.f, Nz::Color::Blue);
|
//debugDrawer.DrawLine(lightNode.GetPosition(Nz::CoordSys::Global), lightNode.GetForward() * 10.f, Nz::Color::Blue);
|
||||||
/*Nz::Boxf test = spotLight->GetBoundingVolume().aabb;
|
Nz::Vector3f pos = lightNode.GetPosition(Nz::CoordSys::Global);
|
||||||
debugDrawer.DrawBox(test, Nz::Color::Blue);
|
debugDrawer.DrawBox(Nz::Boxf(pos.x - 0.05f, pos.y - 0.05f, pos.z - 0.05f, 0.1f, 0.1f, 0.1f), Nz::Color::Blue);
|
||||||
debugDrawer.DrawBox(floorBox, Nz::Color::Red);
|
/*debugDrawer.DrawBox(floorBox, Nz::Color::Red);
|
||||||
Nz::Boxf intersection;
|
Nz::Boxf intersection;
|
||||||
if (floorBox.Intersect(test, &intersection))
|
if (floorBox.Intersect(test, &intersection))
|
||||||
debugDrawer.DrawBox(intersection, Nz::Color::Green);*/
|
debugDrawer.DrawBox(intersection, Nz::Color::Green);*/
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ namespace Nz
|
||||||
bool HasAttachment(const std::vector<FramePass::Input>& inputs, std::size_t attachmentIndex) const;
|
bool HasAttachment(const std::vector<FramePass::Input>& inputs, std::size_t attachmentIndex) const;
|
||||||
void RemoveDuplicatePasses();
|
void RemoveDuplicatePasses();
|
||||||
std::size_t ResolveAttachmentIndex(std::size_t attachmentIndex) const;
|
std::size_t ResolveAttachmentIndex(std::size_t attachmentIndex) const;
|
||||||
|
void RegisterPassInput(std::size_t passIndex, std::size_t attachmentIndex);
|
||||||
std::size_t RegisterTexture(std::size_t attachmentIndex);
|
std::size_t RegisterTexture(std::size_t attachmentIndex);
|
||||||
void ReorderPasses();
|
void ReorderPasses();
|
||||||
void TraverseGraph(std::size_t passIndex);
|
void TraverseGraph(std::size_t passIndex);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <Nazara/Graphics/Config.hpp>
|
#include <Nazara/Graphics/Config.hpp>
|
||||||
#include <Nazara/Graphics/FramePassAttachment.hpp>
|
#include <Nazara/Graphics/FramePassAttachment.hpp>
|
||||||
#include <Nazara/Math/Rect.hpp>
|
#include <Nazara/Math/Rect.hpp>
|
||||||
|
#include <Nazara/Renderer/Enums.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
@ -73,6 +74,7 @@ namespace Nz
|
||||||
inline void SetDepthStencilInput(std::size_t attachmentId);
|
inline void SetDepthStencilInput(std::size_t attachmentId);
|
||||||
inline void SetDepthStencilOutput(std::size_t attachmentId);
|
inline void SetDepthStencilOutput(std::size_t attachmentId);
|
||||||
inline void SetExecutionCallback(ExecutionCallback callback);
|
inline void SetExecutionCallback(ExecutionCallback callback);
|
||||||
|
inline void SetInputLayout(std::size_t inputIndex, TextureLayout layout);
|
||||||
inline void SetReadInput(std::size_t inputIndex, bool doesRead);
|
inline void SetReadInput(std::size_t inputIndex, bool doesRead);
|
||||||
|
|
||||||
FramePass& operator=(const FramePass&) = delete;
|
FramePass& operator=(const FramePass&) = delete;
|
||||||
|
|
@ -89,6 +91,7 @@ namespace Nz
|
||||||
struct Input
|
struct Input
|
||||||
{
|
{
|
||||||
std::size_t attachmentId;
|
std::size_t attachmentId;
|
||||||
|
std::optional<TextureLayout> assumedLayout;
|
||||||
bool doesRead = true;
|
bool doesRead = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,12 @@ namespace Nz
|
||||||
m_executionCallback = std::move(callback);
|
m_executionCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void FramePass::SetInputLayout(std::size_t inputIndex, TextureLayout layout)
|
||||||
|
{
|
||||||
|
assert(inputIndex < m_inputs.size());
|
||||||
|
m_inputs[inputIndex].assumedLayout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
inline void FramePass::SetReadInput(std::size_t inputIndex, bool doesRead)
|
inline void FramePass::SetReadInput(std::size_t inputIndex, bool doesRead)
|
||||||
{
|
{
|
||||||
assert(inputIndex < m_inputs.size());
|
assert(inputIndex < m_inputs.size());
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ namespace Nz
|
||||||
m_position = position;
|
m_position = position;
|
||||||
|
|
||||||
UpdateBoundingVolume();
|
UpdateBoundingVolume();
|
||||||
|
OnLightTransformInvalided(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void PointLight::UpdateRadius(float radius)
|
inline void PointLight::UpdateRadius(float radius)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Graphics module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_GRAPHICS_POINTLIGHTSHADOWDATA_HPP
|
||||||
|
#define NAZARA_GRAPHICS_POINTLIGHTSHADOWDATA_HPP
|
||||||
|
|
||||||
|
#include <Nazara/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 <array>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class FramePipeline;
|
||||||
|
class PointLight;
|
||||||
|
|
||||||
|
class NAZARA_GRAPHICS_API PointLightShadowData : public LightShadowData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PointLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const PointLight& light);
|
||||||
|
PointLightShadowData(const PointLightShadowData&) = delete;
|
||||||
|
PointLightShadowData(PointLightShadowData&&) = delete;
|
||||||
|
~PointLightShadowData() = default;
|
||||||
|
|
||||||
|
void PrepareRendering(RenderFrame& renderFrame) override;
|
||||||
|
|
||||||
|
void RegisterMaterialInstance(const MaterialInstance& matInstance) override;
|
||||||
|
void RegisterPassInputs(FramePass& pass) override;
|
||||||
|
void RegisterToFrameGraph(FrameGraph& frameGraph) override;
|
||||||
|
|
||||||
|
const Texture* RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const override;
|
||||||
|
|
||||||
|
void UnregisterMaterialInstance(const MaterialInstance& matInstance) override;
|
||||||
|
|
||||||
|
PointLightShadowData& operator=(const PointLightShadowData&) = delete;
|
||||||
|
PointLightShadowData& operator=(PointLightShadowData&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NazaraSlot(Light, OnLightShadowMapSettingChange, m_onLightShadowMapSettingChange);
|
||||||
|
NazaraSlot(Light, OnLightTransformInvalided, m_onLightTransformInvalidated);
|
||||||
|
|
||||||
|
struct DirectionData
|
||||||
|
{
|
||||||
|
std::optional<DepthPipelinePass> depthPass;
|
||||||
|
std::size_t attachmentIndex;
|
||||||
|
ShadowViewer viewer;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<DirectionData, 6> m_directions;
|
||||||
|
std::size_t m_cubeAttachmentIndex;
|
||||||
|
FramePipeline& m_pipeline;
|
||||||
|
const PointLight& m_light;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Graphics/PointLightShadowData.inl>
|
||||||
|
|
||||||
|
#endif // NAZARA_GRAPHICS_POINTLIGHTSHADOWDATA_HPP
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Graphics module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Graphics/PointLightShadowData.hpp>
|
||||||
|
#include <Nazara/Graphics/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Graphics/DebugOff.hpp>
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Nz
|
||||||
class NAZARA_GRAPHICS_API ShadowViewer : public AbstractViewer
|
class NAZARA_GRAPHICS_API ShadowViewer : public AbstractViewer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline ShadowViewer(const Recti& viewport, UInt32 renderMask);
|
ShadowViewer() = default;
|
||||||
ShadowViewer(const ShadowViewer&) = delete;
|
ShadowViewer(const ShadowViewer&) = delete;
|
||||||
ShadowViewer(ShadowViewer&&) = delete;
|
ShadowViewer(ShadowViewer&&) = delete;
|
||||||
~ShadowViewer() = default;
|
~ShadowViewer() = default;
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,6 @@
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
inline ShadowViewer::ShadowViewer(const Recti& viewport, UInt32 renderMask) :
|
|
||||||
m_renderMask(renderMask)
|
|
||||||
{
|
|
||||||
UpdateViewport(viewport);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ShadowViewer::UpdateRenderMask(UInt32 renderMask)
|
inline void ShadowViewer::UpdateRenderMask(UInt32 renderMask)
|
||||||
{
|
{
|
||||||
m_renderMask = renderMask;
|
m_renderMask = renderMask;
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Nz::GL
|
||||||
const Context& context = EnsureDeviceContext();
|
const Context& context = EnsureDeviceContext();
|
||||||
|
|
||||||
if (context.glObjectLabel)
|
if (context.glObjectLabel)
|
||||||
context.glObjectLabel(ObjectType, m_objectId, name.size(), name.data());
|
context.glObjectLabel(ObjectType, m_objectId, SafeCast<GLsizei>(name.size()), name.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -712,9 +712,13 @@ namespace Nz
|
||||||
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId);
|
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId);
|
||||||
|
|
||||||
TextureLayout& textureLayout = textureLayouts[textureId];
|
TextureLayout& textureLayout = textureLayouts[textureId];
|
||||||
assert(textureLayouts[textureId] != TextureLayout::Undefined);
|
if (!input.assumedLayout)
|
||||||
|
{
|
||||||
textureLayout = TextureLayout::ColorInput;
|
assert(textureLayouts[textureId] != TextureLayout::Undefined);
|
||||||
|
textureLayout = TextureLayout::ColorInput;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
textureLayout = *input.assumedLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto RegisterColorOutput = [&](const FramePass::Output& output, bool shouldLoad)
|
auto RegisterColorOutput = [&](const FramePass::Output& output, bool shouldLoad)
|
||||||
|
|
@ -949,7 +953,21 @@ namespace Nz
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameGraph::RegisterPassInput(std::size_t passIndex, std::size_t attachmentIndex)
|
||||||
|
{
|
||||||
|
auto it = m_pending.attachmentWriteList.find(attachmentIndex);
|
||||||
|
if (it != m_pending.attachmentWriteList.end())
|
||||||
|
{
|
||||||
|
const PassList& dependencyPassList = it->second;
|
||||||
|
for (std::size_t dependencyPass : dependencyPassList)
|
||||||
|
{
|
||||||
|
if (dependencyPass != passIndex)
|
||||||
|
TraverseGraph(dependencyPass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t FrameGraph::RegisterTexture(std::size_t attachmentIndex)
|
std::size_t FrameGraph::RegisterTexture(std::size_t attachmentIndex)
|
||||||
{
|
{
|
||||||
if (auto it = m_pending.attachmentToTextures.find(attachmentIndex); it != m_pending.attachmentToTextures.end())
|
if (auto it = m_pending.attachmentToTextures.find(attachmentIndex); it != m_pending.attachmentToTextures.end())
|
||||||
|
|
@ -1126,31 +1144,9 @@ namespace Nz
|
||||||
|
|
||||||
const FramePass& framePass = m_framePasses[passIndex];
|
const FramePass& framePass = m_framePasses[passIndex];
|
||||||
for (const auto& input : framePass.GetInputs())
|
for (const auto& input : framePass.GetInputs())
|
||||||
{
|
RegisterPassInput(passIndex, input.attachmentId);
|
||||||
auto it = m_pending.attachmentWriteList.find(input.attachmentId);
|
|
||||||
if (it != m_pending.attachmentWriteList.end())
|
|
||||||
{
|
|
||||||
const PassList& dependencyPassList = it->second;
|
|
||||||
for (std::size_t dependencyPass : dependencyPassList)
|
|
||||||
{
|
|
||||||
if (dependencyPass != passIndex)
|
|
||||||
TraverseGraph(dependencyPass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::size_t dsInput = framePass.GetDepthStencilInput(); dsInput != FramePass::InvalidAttachmentId)
|
if (std::size_t dsInput = framePass.GetDepthStencilInput(); dsInput != FramePass::InvalidAttachmentId)
|
||||||
{
|
RegisterPassInput(passIndex, dsInput);
|
||||||
auto it = m_pending.attachmentWriteList.find(dsInput);
|
|
||||||
if (it != m_pending.attachmentWriteList.end())
|
|
||||||
{
|
|
||||||
const PassList& dependencyPassList = it->second;
|
|
||||||
for (std::size_t dependencyPass : dependencyPassList)
|
|
||||||
{
|
|
||||||
if (dependencyPass != passIndex)
|
|
||||||
TraverseGraph(dependencyPass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <Nazara/Graphics/PointLight.hpp>
|
#include <Nazara/Graphics/PointLight.hpp>
|
||||||
#include <Nazara/Graphics/Enums.hpp>
|
#include <Nazara/Graphics/Enums.hpp>
|
||||||
#include <Nazara/Graphics/Graphics.hpp>
|
#include <Nazara/Graphics/Graphics.hpp>
|
||||||
|
#include <Nazara/Graphics/PointLightShadowData.hpp>
|
||||||
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
#include <Nazara/Graphics/PredefinedShaderStructs.hpp>
|
||||||
#include <Nazara/Math/Vector2.hpp>
|
#include <Nazara/Math/Vector2.hpp>
|
||||||
#include <Nazara/Math/Vector3.hpp>
|
#include <Nazara/Math/Vector3.hpp>
|
||||||
|
|
@ -26,13 +27,14 @@ namespace Nz
|
||||||
AccessByOffset<UInt32&>(data, lightOffset.lightMemberOffsets.type) = UnderlyingCast(BasicLightType::Point);
|
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<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<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.parameter1) = Vector4f(m_position.x, m_position.y, m_position.z, 0.f);
|
||||||
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = Vector2f(-1.f, -1.f);
|
AccessByOffset<Vector4f&>(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_radius, m_invRadius, 0.f, 0.f);
|
||||||
|
AccessByOffset<Vector2f&>(data, lightOffset.lightMemberOffsets.shadowMapSize) = (IsShadowCaster()) ? Vector2f(1.f / GetShadowMapSize()) : Vector2f(-1.f, -1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
|
std::unique_ptr<LightShadowData> PointLight::InstanciateShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry) const
|
||||||
{
|
{
|
||||||
return nullptr; //< TODO
|
return std::make_unique<PointLightShadowData>(pipeline, elementRegistry, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PointLight::UpdateTransform(const Vector3f& position, const Quaternionf& /*rotation*/, const Vector3f& /*scale*/)
|
void PointLight::UpdateTransform(const Vector3f& position, const Quaternionf& /*rotation*/, const Vector3f& /*scale*/)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||||
|
// This file is part of the "Nazara Engine - Graphics module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Graphics/PointLightShadowData.hpp>
|
||||||
|
#include <Nazara/Graphics/BakedFrameGraph.hpp>
|
||||||
|
#include <Nazara/Graphics/FrameGraph.hpp>
|
||||||
|
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||||
|
#include <Nazara/Graphics/Graphics.hpp>
|
||||||
|
#include <Nazara/Graphics/PointLight.hpp>
|
||||||
|
#include <Nazara/Math/Quaternion.hpp>
|
||||||
|
#include <Nazara/Graphics/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// TODO: Make it constexpr
|
||||||
|
std::array s_dirRotations = {
|
||||||
|
Quaternionf::RotationBetween(Vector3f::Forward(), Vector3f::UnitX()),
|
||||||
|
Quaternionf::RotationBetween(Vector3f::Forward(), -Vector3f::UnitX()),
|
||||||
|
Quaternionf::RotationBetween(Vector3f::Forward(), Vector3f::UnitY()),
|
||||||
|
Quaternionf::RotationBetween(Vector3f::Forward(), -Vector3f::UnitY()),
|
||||||
|
Quaternionf::Identity(),
|
||||||
|
Quaternionf(0.f, 0.f, 1.f, 0.f)
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array<std::string_view, 6> s_dirNames = {
|
||||||
|
"Point-light shadow mapping +X",
|
||||||
|
"Point-light shadow mapping -X",
|
||||||
|
"Point-light shadow mapping +Y",
|
||||||
|
"Point-light shadow mapping -Y",
|
||||||
|
"Point-light shadow mapping +Z",
|
||||||
|
"Point-light shadow mapping -Z"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PointLightShadowData::PointLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const PointLight& light) :
|
||||||
|
m_pipeline(pipeline),
|
||||||
|
m_light(light)
|
||||||
|
{
|
||||||
|
m_onLightShadowMapSettingChange.Connect(m_light.OnLightShadowMapSettingChange, [this](Light* /*light*/, PixelFormat /*newPixelFormat*/, UInt32 newSize)
|
||||||
|
{
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
direction.viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(newSize), SafeCast<int>(newSize)));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_onLightTransformInvalidated.Connect(m_light.OnLightTransformInvalided, [this]([[maybe_unused]] Light* light)
|
||||||
|
{
|
||||||
|
assert(&m_light == light);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_directions.size(); ++i)
|
||||||
|
{
|
||||||
|
DirectionData& direction = m_directions[i];
|
||||||
|
|
||||||
|
ViewerInstance& viewerInstance = direction.viewer.GetViewerInstance();
|
||||||
|
viewerInstance.UpdateEyePosition(m_light.GetPosition());
|
||||||
|
viewerInstance.UpdateViewMatrix(Matrix4f::TransformInverse(m_light.GetPosition(), s_dirRotations[i]));
|
||||||
|
|
||||||
|
m_pipeline.QueueTransfer(&viewerInstance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass");
|
||||||
|
|
||||||
|
UInt32 shadowMapSize = light.GetShadowMapSize();
|
||||||
|
for (std::size_t i = 0; i < m_directions.size(); ++i)
|
||||||
|
{
|
||||||
|
ShadowViewer& viewer = m_directions[i].viewer;
|
||||||
|
|
||||||
|
viewer.UpdateRenderMask(0xFFFFFFFF);
|
||||||
|
viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
|
||||||
|
|
||||||
|
ViewerInstance& viewerInstance = viewer.GetViewerInstance();
|
||||||
|
viewerInstance.UpdateProjectionMatrix(Matrix4f::Perspective(RadianAnglef(HalfPi<float>), 1.f, 0.01f, m_light.GetRadius()));
|
||||||
|
viewerInstance.UpdateEyePosition(m_light.GetPosition());
|
||||||
|
viewerInstance.UpdateViewMatrix(Matrix4f::TransformInverse(m_light.GetPosition(), s_dirRotations[i]));
|
||||||
|
|
||||||
|
m_pipeline.QueueTransfer(&viewerInstance);
|
||||||
|
|
||||||
|
m_directions[i].depthPass.emplace(m_pipeline, elementRegistry, &viewer, shadowPassIndex, std::string(s_dirNames[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pipeline.ForEachRegisteredMaterialInstance([this](const MaterialInstance& matInstance)
|
||||||
|
{
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
direction.depthPass->RegisterMaterialInstance(matInstance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointLightShadowData::PrepareRendering(RenderFrame& renderFrame)
|
||||||
|
{
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
{
|
||||||
|
const Matrix4f& viewProjMatrix = direction.viewer.GetViewerInstance().GetViewProjMatrix();
|
||||||
|
|
||||||
|
Frustumf frustum = Frustumf::Extract(viewProjMatrix);
|
||||||
|
|
||||||
|
std::size_t visibilityHash = 5U;
|
||||||
|
const auto& visibleRenderables = m_pipeline.FrustumCull(frustum, 0xFFFFFFFF, visibilityHash);
|
||||||
|
|
||||||
|
direction.depthPass->Prepare(renderFrame, frustum, visibleRenderables, visibilityHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointLightShadowData::RegisterMaterialInstance(const MaterialInstance& matInstance)
|
||||||
|
{
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
direction.depthPass->RegisterMaterialInstance(matInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointLightShadowData::RegisterPassInputs(FramePass& pass)
|
||||||
|
{
|
||||||
|
std::size_t cubeInputIndex = pass.AddInput(m_cubeAttachmentIndex);
|
||||||
|
pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput);
|
||||||
|
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
pass.AddInput(direction.attachmentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointLightShadowData::RegisterToFrameGraph(FrameGraph& frameGraph)
|
||||||
|
{
|
||||||
|
UInt32 shadowMapSize = m_light.GetShadowMapSize();
|
||||||
|
|
||||||
|
m_cubeAttachmentIndex = frameGraph.AddAttachmentCube({
|
||||||
|
"Point-light shadowmap",
|
||||||
|
m_light.GetShadowMapFormat(),
|
||||||
|
FramePassAttachmentSize::Fixed,
|
||||||
|
shadowMapSize, shadowMapSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_directions.size(); ++i)
|
||||||
|
{
|
||||||
|
DirectionData& direction = m_directions[i];
|
||||||
|
direction.attachmentIndex = frameGraph.AddAttachmentCubeFace(m_cubeAttachmentIndex, static_cast<CubemapFace>(i));
|
||||||
|
direction.depthPass->RegisterToFrameGraph(frameGraph, direction.attachmentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Texture* PointLightShadowData::RetrieveLightShadowmap(const BakedFrameGraph& bakedGraph) const
|
||||||
|
{
|
||||||
|
return bakedGraph.GetAttachmentTexture(m_cubeAttachmentIndex).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointLightShadowData::UnregisterMaterialInstance(const MaterialInstance& matInstance)
|
||||||
|
{
|
||||||
|
for (DirectionData& direction : m_directions)
|
||||||
|
direction.depthPass->UnregisterMaterialInstance(matInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ external
|
||||||
[tag("SkeletalData")] skeletalData: uniform[SkeletalData],
|
[tag("SkeletalData")] skeletalData: uniform[SkeletalData],
|
||||||
[tag("LightData")] lightData: uniform[LightData],
|
[tag("LightData")] lightData: uniform[LightData],
|
||||||
[tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount],
|
[tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount],
|
||||||
[tag("ShadowMapsCube")] shadowMapsCube: array[depth_sampler_cube[f32], MaxLightCount]
|
[tag("ShadowMapsCube")] shadowMapsCube: array[sampler_cube[f32], MaxLightCount]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertToFrag
|
struct VertToFrag
|
||||||
|
|
@ -118,6 +118,11 @@ struct FragOut
|
||||||
[location(0)] RenderTarget0: vec4[f32]
|
[location(0)] RenderTarget0: vec4[f32]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn LinearizeDepth(depth: f32, zNear: f32, zFar: f32) -> f32
|
||||||
|
{
|
||||||
|
return zNear * zFar / (zFar + depth * (zNear - zFar));
|
||||||
|
}
|
||||||
|
|
||||||
[entry(frag), cond(!DepthPass || AlphaTest)]
|
[entry(frag), cond(!DepthPass || AlphaTest)]
|
||||||
fn main(input: VertToFrag) -> FragOut
|
fn main(input: VertToFrag) -> FragOut
|
||||||
{
|
{
|
||||||
|
|
@ -189,25 +194,60 @@ fn main(input: VertToFrag) -> FragOut
|
||||||
else if (light.type == PointLight)
|
else if (light.type == PointLight)
|
||||||
{
|
{
|
||||||
let lightPos = light.parameter1.xyz;
|
let lightPos = light.parameter1.xyz;
|
||||||
let lightInvRadius = light.parameter1.w;
|
let lightRadius = light.parameter2.x;
|
||||||
|
let lightInvRadius = light.parameter2.y;
|
||||||
|
|
||||||
let lightToPos = input.worldPos - lightPos;
|
let lightToPos = input.worldPos - lightPos;
|
||||||
let dist = length(lightToPos);
|
let dist = length(lightToPos);
|
||||||
let lightToPosNorm = lightToPos / max(dist, 0.0001);
|
let lightToPosNorm = lightToPos / max(dist, 0.0001);
|
||||||
|
|
||||||
let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0);
|
let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0);
|
||||||
|
|
||||||
lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb;
|
|
||||||
|
|
||||||
let lambert = max(dot(normal, -lightToPosNorm), 0.0);
|
let lambert = max(dot(normal, -lightToPosNorm), 0.0);
|
||||||
|
|
||||||
lightDiffuse += attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor;
|
|
||||||
|
|
||||||
let reflection = reflect(lightToPosNorm, normal);
|
let reflection = reflect(lightToPosNorm, normal);
|
||||||
let specFactor = max(dot(reflection, eyeVec), 0.0);
|
let specFactor = max(dot(reflection, eyeVec), 0.0);
|
||||||
specFactor = pow(specFactor, settings.Shininess);
|
specFactor = pow(specFactor, settings.Shininess);
|
||||||
|
|
||||||
lightSpecular += attenuationFactor * specFactor * light.color.rgb;
|
let shadowFactor = 1.0;
|
||||||
|
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 bias = 0.05;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
[unroll]
|
||||||
|
for x in 0 -> sampleCount
|
||||||
|
{
|
||||||
|
[unroll]
|
||||||
|
for y 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 depth = shadowMapsCube[i].Sample(sampleDir).r;
|
||||||
|
depth = LinearizeDepth(depth, 0.01, lightRadius);
|
||||||
|
|
||||||
|
if (depth > 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)
|
else if (light.type == SpotLight)
|
||||||
{
|
{
|
||||||
|
|
@ -233,10 +273,11 @@ fn main(input: VertToFrag) -> FragOut
|
||||||
let specFactor = max(dot(reflection, eyeVec), 0.0);
|
let specFactor = max(dot(reflection, eyeVec), 0.0);
|
||||||
specFactor = pow(specFactor, settings.Shininess);
|
specFactor = pow(specFactor, settings.Shininess);
|
||||||
|
|
||||||
let shadowFactor = 0.0;
|
let shadowFactor = 1.0;
|
||||||
if (light.invShadowMapSize.x > 0.0)
|
if (light.invShadowMapSize.x > 0.0)
|
||||||
{
|
{
|
||||||
let shadowCoords = input.lightProjPos[i].xyz / input.lightProjPos[i].w;
|
let shadowCoords = input.lightProjPos[i].xyz / input.lightProjPos[i].w;
|
||||||
|
shadowFactor = 0.0;
|
||||||
[unroll]
|
[unroll]
|
||||||
for x in -1 -> 2
|
for x in -1 -> 2
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ namespace Nz
|
||||||
{
|
{
|
||||||
SpotLightShadowData::SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light) :
|
SpotLightShadowData::SpotLightShadowData(FramePipeline& pipeline, ElementRendererRegistry& elementRegistry, const SpotLight& light) :
|
||||||
m_pipeline(pipeline),
|
m_pipeline(pipeline),
|
||||||
m_light(light),
|
m_light(light)
|
||||||
m_viewer(Recti(0, 0, 1, 1), 0xFFFFFFFF)
|
|
||||||
{
|
{
|
||||||
UInt32 shadowMapSize = light.GetShadowMapSize();
|
UInt32 shadowMapSize = light.GetShadowMapSize();
|
||||||
|
m_viewer.UpdateRenderMask(0xFFFFFFFF);
|
||||||
m_viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
|
m_viewer.UpdateViewport(Recti(0, 0, SafeCast<int>(shadowMapSize), SafeCast<int>(shadowMapSize)));
|
||||||
|
|
||||||
ViewerInstance& viewerInstance = m_viewer.GetViewerInstance();
|
ViewerInstance& viewerInstance = m_viewer.GetViewerInstance();
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ namespace Nz
|
||||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||||
bindingEntry.bindingIndex = bindingIndex;
|
bindingEntry.bindingIndex = bindingIndex;
|
||||||
bindingEntry.content = ShaderBinding::TextureBindings {
|
bindingEntry.content = ShaderBinding::TextureBindings {
|
||||||
SafeCast<UInt32>(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex]
|
SafeCast<UInt32>(renderState.shadowMapsCube.size()), &m_textureBindingCache[textureBindingBaseIndex]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace Nz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLShaderBinding::UpdateDebugName(std::string_view name)
|
void OpenGLShaderBinding::UpdateDebugName(std::string_view /*name*/)
|
||||||
{
|
{
|
||||||
// No OpenGL object to name
|
// No OpenGL object to name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue