From e990a320cc3a98e30749b4e987f53ed8c1e4acbc Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 19 Nov 2022 17:10:27 +0100 Subject: [PATCH] WIP --- examples/DeferredShading/main.cpp | 4 +- examples/GraphicsTest/main.cpp | 2 +- examples/RenderTest/main.cpp | 18 ++-- examples/Showcase/main.cpp | 83 +++++++++++++++++-- include/Nazara/Graphics/DepthPipelinePass.hpp | 5 +- include/Nazara/Graphics/ElementRenderer.hpp | 10 +++ include/Nazara/Graphics/Enums.hpp | 2 + .../Nazara/Graphics/ForwardFramePipeline.hpp | 5 +- .../Nazara/Graphics/ForwardPipelinePass.hpp | 20 ++++- include/Nazara/Graphics/FramePipeline.hpp | 3 + include/Nazara/Graphics/Graphics.hpp | 1 + .../Graphics/PredefinedShaderStructs.hpp | 1 + include/Nazara/Graphics/SpotLight.hpp | 2 + include/Nazara/Graphics/SpotLight.inl | 19 ++++- include/Nazara/Graphics/SubmeshRenderer.hpp | 1 + .../OpenGLRenderer/OpenGLShaderBinding.hpp | 3 +- include/Nazara/OpenGLRenderer/Utils.hpp | 2 +- include/Nazara/OpenGLRenderer/Utils.inl | 10 +-- .../OpenGLRenderer/Wrapper/CoreFunctions.hpp | 1 + .../Nazara/Renderer/RenderPipelineLayout.hpp | 3 +- include/Nazara/Renderer/RenderStates.hpp | 8 +- include/Nazara/Renderer/RenderStates.inl | 20 +++-- include/Nazara/Renderer/ShaderBinding.hpp | 8 +- include/Nazara/Renderer/TextureSampler.hpp | 2 + include/Nazara/Renderer/TextureSampler.inl | 8 ++ include/Nazara/Utility/Enums.hpp | 2 +- include/Nazara/VulkanRenderer/Utils.hpp | 2 +- include/Nazara/VulkanRenderer/Utils.inl | 10 +-- plugins/Assimp/Plugin.cpp | 2 +- src/Nazara/Graphics/DepthPipelinePass.cpp | 14 ++-- src/Nazara/Graphics/ForwardFramePipeline.cpp | 34 ++++++-- src/Nazara/Graphics/ForwardPipelinePass.cpp | 48 +++++++++-- src/Nazara/Graphics/FrameGraph.cpp | 2 +- src/Nazara/Graphics/Graphics.cpp | 52 +++++++++++- src/Nazara/Graphics/Material.cpp | 6 ++ .../Graphics/PredefinedShaderStructs.cpp | 1 + .../Resources/Shaders/FullscreenVertex.nzsl | 2 +- .../Shaders/Modules/Engine/LightData.nzsl | 3 +- .../Resources/Shaders/PhongMaterial.nzsl | 48 +++++++++-- src/Nazara/Graphics/ShaderReflection.cpp | 18 ++-- src/Nazara/Graphics/SpotLight.cpp | 1 + src/Nazara/Graphics/SubmeshRenderer.cpp | 57 ++++++++++++- .../OpenGLRenderPipelineLayout.cpp | 10 ++- .../OpenGLRenderer/OpenGLShaderBinding.cpp | 59 ++++++++----- .../OpenGLRenderer/OpenGLTextureSampler.cpp | 6 ++ src/Nazara/OpenGLRenderer/Wrapper/Context.cpp | 63 ++++++++++---- src/Nazara/Renderer/DebugDrawer.cpp | 2 +- src/Nazara/VulkanRenderer/Vulkan.cpp | 6 +- .../VulkanRenderer/VulkanRenderPipeline.cpp | 5 +- .../VulkanRenderPipelineLayout.cpp | 2 +- .../VulkanRenderer/VulkanShaderBinding.cpp | 53 ++++++++++-- src/Nazara/VulkanRenderer/VulkanTexture.cpp | 8 +- .../VulkanRenderer/VulkanTextureSampler.cpp | 2 + test.txt | 13 +++ 54 files changed, 618 insertions(+), 154 deletions(-) create mode 100644 test.txt diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index b6de5c0df..d5fcf0076 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -159,7 +159,7 @@ int main() skyboxPipelineInfo.depthBuffer = true; skyboxPipelineInfo.depthCompare = Nz::RendererComparison::Equal; skyboxPipelineInfo.faceCulling = true; - skyboxPipelineInfo.cullingSide = Nz::FaceSide::Front; + skyboxPipelineInfo.faceCulling = Nz::FaceCulling::Front; skyboxPipelineInfo.pipelineLayout = skyboxPipelineLayout; skyboxPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, shaderDir / "skybox.nzsl", states)); skyboxPipelineInfo.vertexBuffers.push_back({ @@ -558,7 +558,7 @@ int main() }); lightingPipelineInfo.depthBuffer = false; lightingPipelineInfo.faceCulling = true; - lightingPipelineInfo.cullingSide = Nz::FaceSide::Front; + lightingPipelineInfo.faceCulling = Nz::FaceCulling::Front; lightingPipelineInfo.stencilTest = true; lightingPipelineInfo.stencilBack.compare = Nz::RendererComparison::NotEqual; lightingPipelineInfo.stencilBack.fail = Nz::StencilOperation::Zero; diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index c981fe90b..52091a1c1 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -21,7 +21,7 @@ int main() if (std::getchar() == 'y') rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; else - rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; + rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; Nz::Modules nazara(rendererConfig); diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index 8c66c22cb..198c4f583 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -196,11 +196,11 @@ int main() std::shared_ptr basePipelineLayout = device->InstantiateRenderPipelineLayout(pipelineLayoutInfo); - auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back(); - textureBinding.setIndex = 1; - textureBinding.bindingIndex = 0; - textureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; - textureBinding.type = Nz::ShaderBindingType::Texture; + auto& pipelineTextureBinding = pipelineLayoutInfo.bindings.emplace_back(); + pipelineTextureBinding.setIndex = 1; + pipelineTextureBinding.bindingIndex = 0; + pipelineTextureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; + pipelineTextureBinding.type = Nz::ShaderBindingType::Texture; std::shared_ptr renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); @@ -218,11 +218,15 @@ int main() } }); + Nz::ShaderBinding::TextureBinding textureBinding { + texture.get(), textureSampler.get() + }; + textureShaderBinding->Update({ { 0, - Nz::ShaderBinding::TextureBinding { - texture.get(), textureSampler.get() + Nz::ShaderBinding::TextureBindings { + 1, &textureBinding } } }); diff --git a/examples/Showcase/main.cpp b/examples/Showcase/main.cpp index c5fa2ce3b..d11ec8de9 100644 --- a/examples/Showcase/main.cpp +++ b/examples/Showcase/main.cpp @@ -31,7 +31,11 @@ int main() resourceDir = "../.." / resourceDir; Nz::Renderer::Config rendererConfig; - rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; + std::cout << "Run using Vulkan? (y/n)" << std::endl; + if (std::getchar() == 'y') + rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; + else + rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; Nz::Modules nazara(rendererConfig); @@ -70,7 +74,7 @@ int main() playerRotNode.SetParent(playerNode); auto& cameraNode = registry.emplace(playerCamera); - cameraNode.SetParent(playerRotNode); + //cameraNode.SetParent(playerRotNode); auto& cameraComponent = registry.emplace(playerCamera, window.GetRenderTarget()); cameraComponent.UpdateZNear(0.2f); @@ -160,10 +164,11 @@ int main() entt::handle bobEntity = entt::handle(registry, registry.create()); entt::entity bobLight = registry.create(); + { auto& lightNode = registry.emplace(bobLight); - lightNode.SetPosition(Nz::Vector3f::Up() * 3.f); - lightNode.SetRotation(Nz::EulerAnglesf(-90.f, 0.f, 0.f)); + lightNode.SetPosition(Nz::Vector3f::Up() * 3.f + Nz::Vector3f::Backward() * 1.f); + lightNode.SetRotation(Nz::EulerAnglesf(-70.f, 0.f, 0.f)); auto spotLight = std::make_shared(); spotLight->UpdateAmbientFactor(1.f); @@ -228,6 +233,36 @@ int main() registry.emplace(smallBobEntity, skeleton); } + std::shared_ptr textMat = Nz::Graphics::Instance()->GetDefaultMaterials().phongMaterial->Instantiate(); + textMat->UpdatePassStates("ForwardPass", [](Nz::RenderStates& renderStates) + { + renderStates.depthWrite = false; + renderStates.blending = true; + renderStates.faceCulling = Nz::FaceCulling::None; + renderStates.blend.modeColor = Nz::BlendEquation::Add; + renderStates.blend.modeAlpha = Nz::BlendEquation::Add; + renderStates.blend.srcColor = Nz::BlendFunc::SrcAlpha; + renderStates.blend.dstColor = Nz::BlendFunc::InvSrcAlpha; + renderStates.blend.srcAlpha = Nz::BlendFunc::One; + renderStates.blend.dstAlpha = Nz::BlendFunc::One; + return true; + }); + + textMat->SetValueProperty("AlphaTest", true); + + std::shared_ptr sprite = std::make_shared(textMat); + sprite->UpdateRenderLayer(1); + sprite->Update(Nz::SimpleTextDrawer::Draw("Shadow-mapping !", 72), 0.002f); + + entt::entity textEntity = registry.create(); + { + auto& entityGfx = registry.emplace(textEntity); + entityGfx.AttachRenderable(sprite, 1); + + auto& entityNode = registry.emplace(textEntity); + entityNode.SetPosition(Nz::Vector3f::Up() * 0.5f + Nz::Vector3f::Backward() * 0.66f + Nz::Vector3f::Left() * 0.5f); + entityNode.SetRotation(Nz::EulerAnglesf(-45.f, 0.f, 0.f)); + } entt::entity planeEntity = registry.create(); Nz::Boxf floorBox; @@ -264,6 +299,20 @@ int main() auto& planeBody = registry.emplace(planeEntity, &physSytem.GetPhysWorld()); planeBody.SetGeom(std::make_shared(Nz::Vector3f(planeSize.x, 0.5f, planeSize.y), Nz::Vector3f(0.f, -0.25f, 0.f))); + + Nz::Mesh boxMesh; + boxMesh.CreateStatic(); + boxMesh.BuildSubMesh(Nz::Primitive::Box(Nz::Vector3f(0.5f, 0.5f, 0.5f)), meshPrimitiveParams); + boxMesh.SetMaterialCount(1); + + std::shared_ptr boxMeshGfx = Nz::GraphicalMesh::BuildFromMesh(boxMesh); + + std::shared_ptr boxModel = std::make_shared(std::move(boxMeshGfx), boxMesh.GetAABB()); + boxModel->SetMaterial(0, planeMat); + + entt::entity boxEntity = registry.create(); + registry.emplace(boxEntity).SetPosition(Nz::Vector3f(0.f, 0.25f, -0.5f)); + registry.emplace(boxEntity).AttachRenderable(boxModel, 0xFFFFFFFF); } window.EnableEventPolling(true); @@ -308,7 +357,9 @@ int main() // Idem, mais pour éviter les problèmes de calcul de la matrice de vue, on restreint les angles camAngles.pitch = Nz::Clamp(camAngles.pitch - event.mouseMove.deltaY * sensitivity, -89.f, 89.f); - auto& playerRotNode = registry.get(playerRotation); + /*auto& playerRotNode = registry.get(playerRotation); + playerRotNode.SetRotation(camAngles);*/ + auto& playerRotNode = registry.get(playerCamera); playerRotNode.SetRotation(camAngles); break; } @@ -322,7 +373,7 @@ int main() { float updateTime = updateClock.Restart() / 1'000'000.f; - auto& playerBody = registry.get(playerEntity); + /*auto& playerBody = registry.get(playerEntity); float mass = playerBody.GetMass(); @@ -339,7 +390,25 @@ int main() playerBody.AddForce(Nz::Vector3f::Left() * 25.f * mass, Nz::CoordSys::Local); if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D)) - playerBody.AddForce(Nz::Vector3f::Right() * 25.f * mass, Nz::CoordSys::Local); + playerBody.AddForce(Nz::Vector3f::Right() * 25.f * mass, Nz::CoordSys::Local);*/ + + float cameraSpeed = 2.f; + + auto& cameraNode = registry.get(playerCamera); + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Space)) + cameraNode.Move(Nz::Vector3f::Up() * cameraSpeed * updateTime, Nz::CoordSys::Global); + + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z)) + cameraNode.Move(Nz::Vector3f::Forward() * cameraSpeed * updateTime, Nz::CoordSys::Local); + + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S)) + cameraNode.Move(Nz::Vector3f::Backward() * cameraSpeed * updateTime, Nz::CoordSys::Local); + + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q)) + cameraNode.Move(Nz::Vector3f::Left() * cameraSpeed * updateTime, Nz::CoordSys::Local); + + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D)) + cameraNode.Move(Nz::Vector3f::Right() * cameraSpeed * updateTime, Nz::CoordSys::Local); if (!paused) { diff --git a/include/Nazara/Graphics/DepthPipelinePass.hpp b/include/Nazara/Graphics/DepthPipelinePass.hpp index 4a50f9b80..fe0830a3e 100644 --- a/include/Nazara/Graphics/DepthPipelinePass.hpp +++ b/include/Nazara/Graphics/DepthPipelinePass.hpp @@ -30,7 +30,7 @@ namespace Nz class NAZARA_GRAPHICS_API DepthPipelinePass : public FramePipelinePass { public: - DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer); + DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer, std::size_t passIndex, std::string passName); DepthPipelinePass(const DepthPipelinePass&) = delete; DepthPipelinePass(DepthPipelinePass&&) = delete; ~DepthPipelinePass() = default; @@ -57,8 +57,9 @@ namespace Nz NazaraSlot(MaterialInstance, OnMaterialInstanceShaderBindingInvalidated, onMaterialInstanceShaderBindingInvalidated); }; - std::size_t m_depthPassIndex; + std::size_t m_passIndex; std::size_t m_lastVisibilityHash; + std::string m_passName; std::vector> m_elementRendererData; std::vector m_renderStates; std::vector m_renderElements; diff --git a/include/Nazara/Graphics/ElementRenderer.hpp b/include/Nazara/Graphics/ElementRenderer.hpp index ec55fdc54..be04a5ef4 100644 --- a/include/Nazara/Graphics/ElementRenderer.hpp +++ b/include/Nazara/Graphics/ElementRenderer.hpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -43,6 +45,14 @@ namespace Nz struct RenderStates { + RenderStates() + { + shadowMaps2D.fill(nullptr); + shadowMapsCube.fill(nullptr); + } + + std::array shadowMaps2D; + std::array shadowMapsCube; RenderBufferView lightData; }; }; diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 7419aefa8..75206b4c6 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -95,6 +95,8 @@ namespace Nz InstanceDataUbo, LightDataUbo, OverlayTexture, + Shadowmap2D, + ShadowmapCube, SkeletalDataUbo, ViewerDataUbo, diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 3ffe59030..338b2ece5 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -50,6 +50,9 @@ namespace Nz std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) override; 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; + void Render(RenderFrame& renderFrame) override; void UnregisterLight(std::size_t lightIndex) override; @@ -147,7 +150,7 @@ namespace Nz std::unordered_map m_materialInstances; std::vector m_renderStates; std::vector m_visibleRenderables; - std::vector m_visibleLights; + std::vector m_visibleLights; robin_hood::unordered_set m_transferSet; BakedFrameGraph m_bakedFrameGraph; Bitset m_shadowCastingLights; diff --git a/include/Nazara/Graphics/ForwardPipelinePass.hpp b/include/Nazara/Graphics/ForwardPipelinePass.hpp index 3392a0b7b..b0ba56e0c 100644 --- a/include/Nazara/Graphics/ForwardPipelinePass.hpp +++ b/include/Nazara/Graphics/ForwardPipelinePass.hpp @@ -41,7 +41,7 @@ namespace Nz inline void InvalidateCommandBuffers(); inline void InvalidateElements(); - void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash); + void Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash); void RegisterMaterialInstance(const MaterialInstance& material); FramePass& RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t colorBufferIndex, std::size_t depthBufferIndex, bool hasDepthPrepass); @@ -76,11 +76,25 @@ namespace Nz UploadPool::Allocation* allocation = nullptr; }; + struct LightPerElementData + { + RenderBufferView lightUniformBuffer; + std::array shadowMaps; + std::size_t lightCount; + }; + struct LightUboPool { std::vector> lightUboBuffers; }; + struct RenderableLight + { + const Light* light; + std::size_t lightIndex; + float contributionScore; + }; + std::size_t m_forwardPassIndex; std::size_t m_lastVisibilityHash; std::shared_ptr m_lightUboPool; @@ -88,10 +102,10 @@ namespace Nz std::vector m_renderStates; std::vector m_renderElements; std::unordered_map m_materialInstances; - std::unordered_map m_lightPerRenderElement; + std::unordered_map m_lightPerRenderElement; std::unordered_map m_lightBufferPerLights; std::vector m_lightDataBuffers; - std::vector m_renderableLights; + std::vector m_renderableLights; RenderQueue m_renderQueue; RenderQueueRegistry m_renderQueueRegistry; AbstractViewer* m_viewer; diff --git a/include/Nazara/Graphics/FramePipeline.hpp b/include/Nazara/Graphics/FramePipeline.hpp index 00d3099a0..1597f4a95 100644 --- a/include/Nazara/Graphics/FramePipeline.hpp +++ b/include/Nazara/Graphics/FramePipeline.hpp @@ -39,6 +39,9 @@ namespace Nz virtual std::size_t RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) = 0; virtual std::size_t RegisterWorldInstance(WorldInstancePtr worldInstance) = 0; + virtual const Light* RetrieveLight(std::size_t lightIndex) const = 0; + virtual const Texture* RetrieveLightShadowmap(std::size_t lightIndex) const = 0; + virtual void Render(RenderFrame& renderFrame) = 0; virtual void UnregisterLight(std::size_t lightIndex) = 0; diff --git a/include/Nazara/Graphics/Graphics.hpp b/include/Nazara/Graphics/Graphics.hpp index a1a24e38c..5fc985e36 100644 --- a/include/Nazara/Graphics/Graphics.hpp +++ b/include/Nazara/Graphics/Graphics.hpp @@ -74,6 +74,7 @@ namespace Nz struct DefaultTextures { + std::array, ImageTypeCount> depthTextures; std::array, ImageTypeCount> whiteTextures; }; diff --git a/include/Nazara/Graphics/PredefinedShaderStructs.hpp b/include/Nazara/Graphics/PredefinedShaderStructs.hpp index 2b8c2b551..8542462d0 100644 --- a/include/Nazara/Graphics/PredefinedShaderStructs.hpp +++ b/include/Nazara/Graphics/PredefinedShaderStructs.hpp @@ -24,6 +24,7 @@ namespace Nz std::size_t parameter2; std::size_t parameter3; std::size_t shadowMappingFlag; + std::size_t viewProjMatrix; }; std::size_t lightsOffset; diff --git a/include/Nazara/Graphics/SpotLight.hpp b/include/Nazara/Graphics/SpotLight.hpp index 2f7903aab..1438336cd 100644 --- a/include/Nazara/Graphics/SpotLight.hpp +++ b/include/Nazara/Graphics/SpotLight.hpp @@ -55,9 +55,11 @@ namespace Nz private: inline void UpdateBoundingVolume(); + inline void UpdateViewProjMatrix(); Color m_color; Quaternionf m_rotation; + Matrix4f m_viewProjMatrix; RadianAnglef m_innerAngle; RadianAnglef m_outerAngle; Vector3f m_direction; diff --git a/include/Nazara/Graphics/SpotLight.inl b/include/Nazara/Graphics/SpotLight.inl index b664679a2..2eb85bd9e 100644 --- a/include/Nazara/Graphics/SpotLight.inl +++ b/include/Nazara/Graphics/SpotLight.inl @@ -97,8 +97,6 @@ namespace Nz { m_innerAngle = innerAngle; m_innerAngleCos = m_innerAngle.GetCos(); - - UpdateBoundingVolume(); } inline void SpotLight::UpdateOuterAngle(RadianAnglef outerAngle) @@ -108,6 +106,7 @@ namespace Nz m_outerAngleTan = m_outerAngle.GetTan(); UpdateBoundingVolume(); + UpdateViewProjMatrix(); } inline void SpotLight::UpdatePosition(const Vector3f& position) @@ -115,6 +114,7 @@ namespace Nz m_position = position; UpdateBoundingVolume(); + UpdateViewProjMatrix(); } inline void SpotLight::UpdateRadius(float radius) @@ -123,6 +123,7 @@ namespace Nz m_invRadius = 1.f / m_radius; UpdateBoundingVolume(); + UpdateViewProjMatrix(); } inline void SpotLight::UpdateRotation(const Quaternionf& rotation) @@ -131,6 +132,7 @@ namespace Nz m_direction = rotation * Vector3f::Forward(); UpdateBoundingVolume(); + UpdateViewProjMatrix(); } inline void SpotLight::UpdateBoundingVolume() @@ -158,6 +160,19 @@ namespace Nz Light::UpdateBoundingVolume(boundingVolume); //< will trigger OnLightDataInvalided } + + 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); + + Matrix4f projection = Matrix4f::Perspective(m_outerAngle * 2.f, 1.f, 1.f, m_radius); + Matrix4f view = Matrix4f::TransformInverse(m_position, m_rotation); + + m_viewProjMatrix = view * projection * biasMatrix; + } } #include diff --git a/include/Nazara/Graphics/SubmeshRenderer.hpp b/include/Nazara/Graphics/SubmeshRenderer.hpp index d73abcc5d..7efcb7821 100644 --- a/include/Nazara/Graphics/SubmeshRenderer.hpp +++ b/include/Nazara/Graphics/SubmeshRenderer.hpp @@ -33,6 +33,7 @@ namespace Nz private: std::vector m_bindingCache; + std::vector m_textureBindingCache; RenderElementPool m_submeshPool; }; diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp index 7c99bd406..fcf14c438 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp @@ -16,7 +16,7 @@ namespace Nz { class OpenGLRenderPipelineLayout; - class NAZARA_OPENGLRENDERER_API OpenGLShaderBinding : public ShaderBinding + class NAZARA_OPENGLRENDERER_API OpenGLShaderBinding final : public ShaderBinding { public: inline OpenGLShaderBinding(OpenGLRenderPipelineLayout& owner, std::size_t poolIndex, std::size_t bindingIndex); @@ -38,6 +38,7 @@ namespace Nz OpenGLShaderBinding& operator=(OpenGLShaderBinding&&) = delete; private: + void HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding); void Release() override; OpenGLRenderPipelineLayout& m_owner; diff --git a/include/Nazara/OpenGLRenderer/Utils.hpp b/include/Nazara/OpenGLRenderer/Utils.hpp index 15b365480..c0abb831f 100644 --- a/include/Nazara/OpenGLRenderer/Utils.hpp +++ b/include/Nazara/OpenGLRenderer/Utils.hpp @@ -33,7 +33,7 @@ namespace Nz inline GLenum ToOpenGL(BlendEquation blendEquation); inline GLenum ToOpenGL(BlendFunc blendFunc); inline GLenum ToOpenGL(FaceFilling filling); - inline GLenum ToOpenGL(FaceSide side); + inline GLenum ToOpenGL(FaceCulling side); inline GLenum ToOpenGL(FrontFace face); inline GLenum ToOpenGL(IndexType indexType); inline GLenum ToOpenGL(PrimitiveMode primitiveMode); diff --git a/include/Nazara/OpenGLRenderer/Utils.inl b/include/Nazara/OpenGLRenderer/Utils.inl index 58e9d95cd..71efdb3e7 100644 --- a/include/Nazara/OpenGLRenderer/Utils.inl +++ b/include/Nazara/OpenGLRenderer/Utils.inl @@ -92,16 +92,16 @@ namespace Nz return {}; } - inline GLenum ToOpenGL(FaceSide side) + inline GLenum ToOpenGL(FaceCulling side) { switch (side) { - case FaceSide::None: + case FaceCulling::None: break; - case FaceSide::Back: return GL_BACK; - case FaceSide::Front: return GL_FRONT; - case FaceSide::FrontAndBack: return GL_FRONT_AND_BACK; + case FaceCulling::Back: return GL_BACK; + case FaceCulling::Front: return GL_FRONT; + case FaceCulling::FrontAndBack: return GL_FRONT_AND_BACK; } NazaraError("Unhandled FaceSide 0x" + NumberToString(UnderlyingCast(side), 16)); diff --git a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp index 9df6a61d3..9e9c920db 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp @@ -141,6 +141,7 @@ typedef void (GL_APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLch cb(glLinkProgram, PFNGLLINKPROGRAMPROC) \ cb(glMapBufferRange, PFNGLMAPBUFFERRANGEPROC) \ cb(glPixelStorei, PFNGLPIXELSTOREIPROC) \ + cb(glPolygonOffset, PFNGLPOLYGONOFFSETPROC) \ cb(glProgramBinary, PFNGLPROGRAMBINARYPROC) \ cb(glProgramParameteri, PFNGLPROGRAMPARAMETERIPROC) \ cb(glReadPixels, PFNGLREADPIXELSPROC) \ diff --git a/include/Nazara/Renderer/RenderPipelineLayout.hpp b/include/Nazara/Renderer/RenderPipelineLayout.hpp index 9576e31db..438183e70 100644 --- a/include/Nazara/Renderer/RenderPipelineLayout.hpp +++ b/include/Nazara/Renderer/RenderPipelineLayout.hpp @@ -21,8 +21,9 @@ namespace Nz { struct Binding { - UInt32 setIndex = 0; + UInt32 arraySize = 1; UInt32 bindingIndex; + UInt32 setIndex = 0; ShaderBindingType type; nzsl::ShaderStageTypeFlags shaderStageFlags; }; diff --git a/include/Nazara/Renderer/RenderStates.hpp b/include/Nazara/Renderer/RenderStates.hpp index 79aaab7ee..706ab0596 100644 --- a/include/Nazara/Renderer/RenderStates.hpp +++ b/include/Nazara/Renderer/RenderStates.hpp @@ -20,13 +20,13 @@ namespace Nz struct RenderStates { ColorComponentMask colorWriteMask = ColorComponentAll; + FaceCulling faceCulling = FaceCulling::Back; FaceFilling faceFilling = FaceFilling::Fill; - FaceSide cullingSide = FaceSide::Back; FrontFace frontFace = FrontFace::CounterClockwise; RendererComparison depthCompare = RendererComparison::LessOrEqual; PrimitiveMode primitiveMode = PrimitiveMode::TriangleList; - struct + struct { BlendEquation modeAlpha = BlendEquation::Add; BlendEquation modeColor = BlendEquation::Add; @@ -48,13 +48,15 @@ namespace Nz } stencilBack, stencilFront; bool blending = false; + bool depthBias = false; bool depthBuffer = false; bool depthClamp = false; bool depthWrite = true; - bool faceCulling = false; bool scissorTest = false; bool stencilTest = false; + float depthBiasConstantFactor = 0.f; + float depthBiasSlopeFactor = 0.f; float lineWidth = 1.f; float pointSize = 1.f; }; diff --git a/include/Nazara/Renderer/RenderStates.inl b/include/Nazara/Renderer/RenderStates.inl index bb0e4167c..b8e8b742d 100644 --- a/include/Nazara/Renderer/RenderStates.inl +++ b/include/Nazara/Renderer/RenderStates.inl @@ -17,9 +17,9 @@ namespace Nz #define NazaraRenderStateFloatMember(field, maxDiff) if (!NumberEquals(lhs.field, rhs.field, maxDiff)) return false NazaraRenderStateBoolMember(blending); + NazaraRenderStateBoolMember(depthBias); NazaraRenderStateBoolMember(depthBuffer); NazaraRenderStateBoolMember(depthClamp); - NazaraRenderStateBoolMember(faceCulling); NazaraRenderStateBoolMember(scissorTest); NazaraRenderStateBoolMember(stencilTest); @@ -27,6 +27,7 @@ namespace Nz NazaraRenderStateBoolMember(depthWrite); NazaraRenderStateMember(colorWriteMask); + NazaraRenderStateMember(faceCulling); NazaraRenderStateMember(faceFilling); if (lhs.blending) //< Remember, at this time we know lhs.blending == rhs.blending @@ -42,8 +43,11 @@ namespace Nz if (lhs.depthBuffer) NazaraRenderStateMember(depthCompare); - if (lhs.faceCulling) - NazaraRenderStateMember(cullingSide); + if (lhs.depthBias) + { + NazaraRenderStateMember(depthBiasConstantFactor); + NazaraRenderStateMember(depthBiasSlopeFactor); + } if (lhs.stencilTest) { @@ -95,15 +99,16 @@ namespace std #define NazaraRenderStateUInt32(member) Nz::HashCombine(seed, pipelineInfo.member) NazaraRenderStateBool(blending); + NazaraRenderStateBool(depthBias); NazaraRenderStateBool(depthBuffer); NazaraRenderStateBool(depthClamp); - NazaraRenderStateBool(faceCulling); NazaraRenderStateBool(scissorTest); NazaraRenderStateBool(stencilTest); NazaraRenderStateBoolDep(depthBuffer, depthWrite); NazaraRenderStateUInt8(colorWriteMask); + NazaraRenderStateEnum(faceCulling); NazaraRenderStateEnum(faceFilling); if (pipelineInfo.blending) //< we don't care about blending state if blending isn't enabled @@ -119,8 +124,11 @@ namespace std if (pipelineInfo.depthBuffer) NazaraRenderStateEnum(depthCompare); - if (pipelineInfo.faceCulling) - NazaraRenderStateEnum(cullingSide); + if (pipelineInfo.depthBias) + { + NazaraRenderStateFloat(depthBiasConstantFactor, 0.001f); + NazaraRenderStateFloat(depthBiasSlopeFactor, 0.001f); + } if (pipelineInfo.stencilTest) //< we don't care about stencil state if stencil isn't enabled { diff --git a/include/Nazara/Renderer/ShaderBinding.hpp b/include/Nazara/Renderer/ShaderBinding.hpp index 1e460a585..8e4c6eedf 100644 --- a/include/Nazara/Renderer/ShaderBinding.hpp +++ b/include/Nazara/Renderer/ShaderBinding.hpp @@ -56,6 +56,12 @@ namespace Nz const TextureSampler* sampler; }; + struct TextureBindings + { + UInt32 arraySize; + const TextureBinding* textureBindings; + }; + struct UniformBufferBinding { RenderBuffer* buffer; @@ -66,7 +72,7 @@ namespace Nz struct Binding { UInt32 bindingIndex; - std::variant content; + std::variant content; }; protected: diff --git a/include/Nazara/Renderer/TextureSampler.hpp b/include/Nazara/Renderer/TextureSampler.hpp index 0b933e78a..9d23df110 100644 --- a/include/Nazara/Renderer/TextureSampler.hpp +++ b/include/Nazara/Renderer/TextureSampler.hpp @@ -24,6 +24,8 @@ namespace Nz SamplerWrap wrapModeU = SamplerWrap::Clamp; SamplerWrap wrapModeV = SamplerWrap::Clamp; SamplerWrap wrapModeW = SamplerWrap::Clamp; + bool depthCompare = false; + RendererComparison depthComparison = RendererComparison::LessOrEqual; inline bool operator==(const TextureSamplerInfo& samplerInfo) const; inline bool operator!=(const TextureSamplerInfo& samplerInfo) const; diff --git a/include/Nazara/Renderer/TextureSampler.inl b/include/Nazara/Renderer/TextureSampler.inl index 5af719f8e..53a3e6380 100644 --- a/include/Nazara/Renderer/TextureSampler.inl +++ b/include/Nazara/Renderer/TextureSampler.inl @@ -32,6 +32,12 @@ namespace Nz if (wrapModeW != samplerInfo.wrapModeW) return false; + if (depthCompare != samplerInfo.depthCompare) + return false; + + if (depthComparison != samplerInfo.depthComparison) + return false; + return true; } @@ -54,6 +60,8 @@ struct std::hash Nz::HashCombine(seed, sampler.wrapModeU); Nz::HashCombine(seed, sampler.wrapModeV); Nz::HashCombine(seed, sampler.wrapModeW); + Nz::HashCombine(seed, sampler.depthCompare); + Nz::HashCombine(seed, sampler.depthComparison); return seed; } diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index f4339d0c5..4267b4601 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -147,7 +147,7 @@ namespace Nz Max = Point }; - enum class FaceSide + enum class FaceCulling { None, diff --git a/include/Nazara/VulkanRenderer/Utils.hpp b/include/Nazara/VulkanRenderer/Utils.hpp index e71dd549b..53f43c069 100644 --- a/include/Nazara/VulkanRenderer/Utils.hpp +++ b/include/Nazara/VulkanRenderer/Utils.hpp @@ -23,7 +23,7 @@ namespace Nz inline VkAttachmentStoreOp ToVulkan(AttachmentStoreOp storeOp); inline VkBufferUsageFlags ToVulkan(BufferType bufferType); inline VkFormat ToVulkan(ComponentType componentType); - inline VkCullModeFlagBits ToVulkan(FaceSide faceSide); + inline VkCullModeFlagBits ToVulkan(FaceCulling faceSide); inline VkPolygonMode ToVulkan(FaceFilling faceFilling); inline VkFrontFace ToVulkan(FrontFace frontFace); inline VkIndexType ToVulkan(IndexType indexType); diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index a7cbefb1d..5dfe4840e 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -130,14 +130,14 @@ namespace Nz return VK_FORMAT_UNDEFINED; } - inline VkCullModeFlagBits ToVulkan(FaceSide faceSide) + inline VkCullModeFlagBits ToVulkan(FaceCulling faceSide) { switch (faceSide) { - case FaceSide::None: return VK_CULL_MODE_NONE; - case FaceSide::Back: return VK_CULL_MODE_BACK_BIT; - case FaceSide::Front: return VK_CULL_MODE_FRONT_BIT; - case FaceSide::FrontAndBack: return VK_CULL_MODE_FRONT_AND_BACK; + case FaceCulling::None: return VK_CULL_MODE_NONE; + case FaceCulling::Back: return VK_CULL_MODE_BACK_BIT; + case FaceCulling::Front: return VK_CULL_MODE_FRONT_BIT; + case FaceCulling::FrontAndBack: return VK_CULL_MODE_FRONT_AND_BACK; } NazaraError("Unhandled FaceSide 0x" + NumberToString(UnderlyingCast(faceSide), 16)); diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index f7008e36c..9247a4d3e 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -52,7 +52,7 @@ SOFTWARE. #include #include -constexpr unsigned int AssimpFlags = aiProcess_CalcTangentSpace | aiProcess_FixInfacingNormals | aiProcess_FlipWindingOrder +constexpr unsigned int AssimpFlags = aiProcess_CalcTangentSpace | aiProcess_FixInfacingNormals | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_GenUVCoords | aiProcess_JoinIdenticalVertices | aiProcess_RemoveComponent | aiProcess_SortByPType | aiProcess_TransformUVCoords | aiProcess_Triangulate; diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp index 72cf9ca04..c938a7835 100644 --- a/src/Nazara/Graphics/DepthPipelinePass.cpp +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -16,15 +15,16 @@ namespace Nz { - DepthPipelinePass::DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer) : + DepthPipelinePass::DepthPipelinePass(FramePipeline& owner, ElementRendererRegistry& elementRegistry, AbstractViewer* viewer, std::size_t passIndex, std::string passName) : + m_passIndex(passIndex), m_lastVisibilityHash(0), + m_passName(std::move(passName)), m_viewer(viewer), m_elementRegistry(elementRegistry), m_pipeline(owner), m_rebuildCommandBuffer(false), m_rebuildElements(false) { - m_depthPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DepthPass"); } void DepthPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, std::size_t visibilityHash) @@ -42,7 +42,7 @@ namespace Nz renderableData.worldInstance }; - renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_depthPassIndex, m_renderElements); + renderableData.instancedRenderable->BuildElement(m_elementRegistry, elementData, m_passIndex, m_renderElements); } m_renderQueueRegistry.Clear(); @@ -105,7 +105,7 @@ namespace Nz void DepthPipelinePass::RegisterMaterialInstance(const MaterialInstance& materialInstance) { - if (!materialInstance.HasPass(m_depthPassIndex)) + if (!materialInstance.HasPass(m_passIndex)) return; auto it = m_materialInstances.find(&materialInstance); @@ -114,7 +114,7 @@ namespace Nz auto& matPassEntry = m_materialInstances[&materialInstance]; matPassEntry.onMaterialInstancePipelineInvalidated.Connect(materialInstance.OnMaterialInstancePipelineInvalidated, [=](const MaterialInstance*, std::size_t passIndex) { - if (passIndex != m_depthPassIndex) + if (passIndex != m_passIndex) return; m_rebuildElements = true; @@ -131,7 +131,7 @@ namespace Nz FramePass& DepthPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t depthBufferIndex) { - FramePass& depthPrepass = frameGraph.AddPass("Depth pre-pass"); + FramePass& depthPrepass = frameGraph.AddPass(m_passName); depthPrepass.SetDepthStencilOutput(depthBufferIndex); depthPrepass.SetDepthStencilClear(1.f, 0); diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 1ade55421..9e7874781 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -177,11 +177,13 @@ namespace Nz std::size_t ForwardFramePipeline::RegisterViewer(AbstractViewer* viewerInstance, Int32 renderOrder) { + std::size_t depthPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("DepthPass"); + std::size_t viewerIndex; auto& viewerData = *m_viewerPool.Allocate(viewerIndex); viewerData.renderOrder = renderOrder; viewerData.debugDrawPass = std::make_unique(*this, viewerInstance); - viewerData.depthPrepass = std::make_unique(*this, m_elementRegistry, viewerInstance); + viewerData.depthPrepass = std::make_unique(*this, m_elementRegistry, viewerInstance, depthPassIndex, "Depth pre-pass"); viewerData.forwardPass = std::make_unique(*this, m_elementRegistry, viewerInstance); viewerData.viewer = viewerInstance; viewerData.onTransferRequired.Connect(viewerInstance->GetViewerInstance().OnTransferRequired, [this](TransferInterface* transferInterface) @@ -211,6 +213,20 @@ namespace Nz return worldInstanceIndex; } + const Light* ForwardFramePipeline::RetrieveLight(std::size_t lightIndex) const + { + return m_lightPool.RetrieveFromIndex(lightIndex)->light.get(); + } + + const Texture* ForwardFramePipeline::RetrieveLightShadowmap(std::size_t lightIndex) const + { + if (!m_shadowCastingLights.UnboundedTest(lightIndex)) + return nullptr; + + std::size_t shadowmapIndex = m_lightPool.RetrieveFromIndex(lightIndex)->shadowMapAttachmentIndex; + return m_bakedFrameGraph.GetAttachmentTexture(shadowmapIndex).get(); + } + void ForwardFramePipeline::Render(RenderFrame& renderFrame) { m_currentRenderFrame = &renderFrame; @@ -355,13 +371,17 @@ namespace Nz m_visibleLights.clear(); for (const LightData& lightData : m_lightPool) + 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(); // TODO: Use more precise tests for point lights (frustum/sphere is cheap) if (renderMask & lightData.renderMask && frustum.Contains(boundingVolume)) { - m_visibleLights.push_back(lightData.light.get()); + m_visibleLights.push_back(lightIndex); visibilityHash = CombineHash(visibilityHash, std::hash()(lightData.light.get())); } } @@ -575,7 +595,7 @@ namespace Nz m_transferSet.insert(transferInterface); }); - lightData->camera->UpdateFOV(spotLight.GetOuterAngle()); + lightData->camera->UpdateFOV(spotLight.GetOuterAngle() * 2.f); lightData->camera->UpdateZFar(spotLight.GetRadius()); lightData->camera->UpdateViewport(Recti(0, 0, SafeCast(shadowMapSize), SafeCast(shadowMapSize))); @@ -584,13 +604,15 @@ namespace Nz if (!lightData->pass) { - lightData->pass = std::make_unique(*this, m_elementRegistry, lightData->camera.get()); + std::size_t shadowPassIndex = Graphics::Instance()->GetMaterialPassRegistry().GetPassIndex("ShadowPass"); + + lightData->pass = std::make_unique(*this, m_elementRegistry, lightData->camera.get(), shadowPassIndex, "Spot shadowmap"); for (RenderableData& renderable : m_renderablePool) { std::size_t matCount = renderable.renderable->GetMaterialCount(); - for (std::size_t i = 0; i < matCount; ++i) + for (std::size_t j = 0; j < matCount; ++j) { - if (MaterialInstance* mat = renderable.renderable->GetMaterial(i).get()) + if (MaterialInstance* mat = renderable.renderable->GetMaterial(j).get()) lightData->pass->RegisterMaterialInstance(*mat); } } diff --git a/src/Nazara/Graphics/ForwardPipelinePass.cpp b/src/Nazara/Graphics/ForwardPipelinePass.cpp index 6727a646e..912f4a353 100644 --- a/src/Nazara/Graphics/ForwardPipelinePass.cpp +++ b/src/Nazara/Graphics/ForwardPipelinePass.cpp @@ -31,7 +31,7 @@ namespace Nz m_lightUboPool = std::make_shared(); } - void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash) + void ForwardPipelinePass::Prepare(RenderFrame& renderFrame, const Frustumf& frustum, const std::vector& visibleRenderables, const std::vector& visibleLights, std::size_t visibilityHash) { if (m_lastVisibilityHash != visibilityHash || m_rebuildElements) //< FIXME { @@ -65,17 +65,22 @@ namespace Nz // Select lights m_renderableLights.clear(); - for (const Light* light : visibleLights) + for (std::size_t lightIndex : visibleLights) { + const Light* light = m_pipeline.RetrieveLight(lightIndex); + const BoundingVolumef& boundingVolume = light->GetBoundingVolume(); if (boundingVolume.Intersect(renderableBoundingVolume.aabb)) - m_renderableLights.push_back(light); + { + float contributionScore = light->ComputeContributionScore(renderableBoundingVolume); + m_renderableLights.push_back({ light, lightIndex, contributionScore }); + } } // Sort lights - std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const Light* lhs, const Light* rhs) + std::sort(m_renderableLights.begin(), m_renderableLights.end(), [&](const RenderableLight& lhs, const RenderableLight& rhs) { - return lhs->ComputeContributionScore(renderableBoundingVolume) < rhs->ComputeContributionScore(renderableBoundingVolume); + return lhs.contributionScore < rhs.contributionScore; }); std::size_t lightCount = std::min(m_renderableLights.size(), MaxLightCountPerDraw); @@ -83,7 +88,7 @@ namespace Nz LightKey lightKey; lightKey.fill(nullptr); for (std::size_t i = 0; i < lightCount; ++i) - lightKey[i] = m_renderableLights[i]; + lightKey[i] = m_renderableLights[i].light; RenderBufferView lightUboView; @@ -130,7 +135,7 @@ namespace Nz UInt8* lightPtr = static_cast(lightDataPtr) + lightOffsets.lightsOffset; for (std::size_t i = 0; i < lightCount; ++i) { - m_renderableLights[i]->FillLightData(lightPtr); + m_renderableLights[i].light->FillLightData(lightPtr); lightPtr += lightOffsets.lightSize; } @@ -155,7 +160,15 @@ namespace Nz for (std::size_t i = previousCount; i < m_renderElements.size(); ++i) { const RenderElement* element = m_renderElements[i].GetElement(); - m_lightPerRenderElement.emplace(element, lightUboView); + + LightPerElementData perElementData; + perElementData.lightCount = lightCount; + perElementData.lightUniformBuffer = lightUboView; + + for (std::size_t i = 0; i < lightCount; ++i) + perElementData.shadowMaps[i] = m_pipeline.RetrieveLightShadowmap(m_renderableLights[i].lightIndex); + + m_lightPerRenderElement.emplace(element, perElementData); } } @@ -222,8 +235,25 @@ namespace Nz auto it = lightPerRenderElement.find(elements[i]); assert(it != lightPerRenderElement.end()); + const LightPerElementData& lightData = it->second; + auto& renderStates = m_renderStates.emplace_back(); - renderStates.lightData = it->second; + renderStates.lightData = lightData.lightUniformBuffer; + + for (std::size_t i = 0; i < lightData.lightCount; ++i) + { + const Texture* texture = lightData.shadowMaps[i]; + if (!texture) + continue; + + if (texture->GetType() == ImageType::E2D) + renderStates.shadowMaps2D[i] = texture; + else + { + assert(texture->GetType() == ImageType::Cubemap); + renderStates.shadowMapsCube[i] = texture; + } + } } elementRenderer.Prepare(viewerInstance, *m_elementRendererData[elementType], renderFrame, elementCount, elements, m_renderStates.data()); diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 08936469c..7b5a15516 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -218,7 +218,7 @@ namespace Nz { if (auto it = m_pending.attachmentToTextures.find(depthStencilOutput); it == m_pending.attachmentToTextures.end()) { - // Special case where multiples attachements point simultaneously to the same texture + // Special case where multiples attachments point simultaneously to the same texture m_pending.attachmentToTextures.emplace(depthStencilOutput, textureId); auto inputIt = m_pending.attachmentLastUse.find(depthStencilInput); diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index cb5ab0032..24783f2d0 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -173,7 +173,7 @@ namespace Nz RenderPipelineLayoutInfo layoutInfo; layoutInfo.bindings.assign({ { - 0, 0, + 1, 0, 0, ShaderBindingType::Texture, nzsl::ShaderStageType::Fragment } @@ -230,7 +230,10 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); - settings.AddPass(shadowPassIndex, depthPass); + + MaterialPass shadowPass = depthPass; + shadowPass.states.faceCulling = FaceCulling::Front; + settings.AddPass(shadowPassIndex, shadowPass); m_defaultMaterials.basicMaterial = std::make_shared(std::move(settings), "BasicMaterial"); } @@ -249,7 +252,10 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); - settings.AddPass(shadowPassIndex, depthPass); + + MaterialPass shadowPass = depthPass; + shadowPass.states.faceCulling = FaceCulling::Front; + settings.AddPass(shadowPassIndex, shadowPass); m_defaultMaterials.pbrMaterial = std::make_shared(std::move(settings), "PhysicallyBasedMaterial"); } @@ -268,7 +274,13 @@ namespace Nz MaterialPass depthPass = forwardPass; depthPass.options[CRC32("DepthPass")] = true; settings.AddPass(depthPassIndex, depthPass); - settings.AddPass(shadowPassIndex, 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); m_defaultMaterials.phongMaterial = std::make_shared(std::move(settings), "PhongMaterial"); } @@ -301,6 +313,38 @@ namespace Nz void Graphics::BuildDefaultTextures() { + // Depth textures (white but with a depth format) + { + PixelFormat depthFormat = PixelFormat::Undefined; + for (PixelFormat depthStencilCandidate : { PixelFormat::Depth16, PixelFormat::Depth24, PixelFormat::Depth32F }) + { + if (m_renderDevice->IsTextureFormatSupported(depthStencilCandidate, TextureUsage::ShaderSampling)) + { + depthFormat = depthStencilCandidate; + break; + } + } + + if (depthFormat == PixelFormat::Undefined) + throw std::runtime_error("couldn't find a sampling-compatible depth pixel format"); + + TextureInfo texInfo; + texInfo.width = texInfo.height = texInfo.depth = texInfo.mipmapLevel = 1; + texInfo.pixelFormat = depthFormat; + + std::array whitePixels = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + for (std::size_t i = 0; i < ImageTypeCount; ++i) + { + texInfo.type = static_cast(i); + if (texInfo.type == ImageType::E3D) + continue; + + m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo); + m_defaultTextures.depthTextures[i]->Update(whitePixels.data()); + } + } + // White texture 2D { TextureInfo texInfo; diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index 26419cc0b..ee67e94bc 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -94,6 +94,12 @@ namespace Nz if (auto it = block->uniformBlocks.find("ViewerData"); it != block->uniformBlocks.end()) m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::ViewerDataUbo)] = it->second.bindingIndex; + if (auto it = block->samplers.find("ShadowMaps2D"); it != block->samplers.end()) + m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::Shadowmap2D)] = it->second.bindingIndex; + + if (auto it = block->samplers.find("ShadowMapsCube"); it != block->samplers.end()) + m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::ShadowmapCube)] = it->second.bindingIndex; + if (auto it = block->uniformBlocks.find("SkeletalData"); it != block->uniformBlocks.end()) m_engineShaderBindings[UnderlyingCast(EngineShaderBinding::SkeletalDataUbo)] = it->second.bindingIndex; diff --git a/src/Nazara/Graphics/PredefinedShaderStructs.cpp b/src/Nazara/Graphics/PredefinedShaderStructs.cpp index 888552ba5..33cc02a99 100644 --- a/src/Nazara/Graphics/PredefinedShaderStructs.cpp +++ b/src/Nazara/Graphics/PredefinedShaderStructs.cpp @@ -21,6 +21,7 @@ namespace Nz lightData.lightMemberOffsets.parameter2 = lightStruct.AddField(nzsl::StructFieldType::Float4); lightData.lightMemberOffsets.parameter3 = lightStruct.AddField(nzsl::StructFieldType::Float4); lightData.lightMemberOffsets.shadowMappingFlag = lightStruct.AddField(nzsl::StructFieldType::Bool1); + lightData.lightMemberOffsets.viewProjMatrix = lightStruct.AddMatrix(nzsl::StructFieldType::Float1, 4, 4, true); lightData.lightSize = lightStruct.GetAlignedSize(); diff --git a/src/Nazara/Graphics/Resources/Shaders/FullscreenVertex.nzsl b/src/Nazara/Graphics/Resources/Shaders/FullscreenVertex.nzsl index 12d57467a..243029639 100644 --- a/src/Nazara/Graphics/Resources/Shaders/FullscreenVertex.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/FullscreenVertex.nzsl @@ -15,8 +15,8 @@ struct VertOut } const vertPos = array[vec2[f32]]( - vec2[f32](-1.0, 1.0), vec2[f32](-1.0, -3.0), + vec2[f32](-1.0, 1.0), vec2[f32]( 3.0, 1.0) ); diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightData.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightData.nzsl index a4a2b699a..1dfcaa64d 100644 --- a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightData.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightData.nzsl @@ -13,7 +13,8 @@ struct Light parameter1: vec4[f32], parameter2: vec4[f32], parameter3: vec4[f32], - hasShadowMapping: u32 + hasShadowMapping: u32, + viewProjMatrix: mat4[f32] } [export] diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index 7d30fcaee..d41457a0d 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -47,6 +47,7 @@ const HasTangent = (VertexTangentLoc >= 0); const HasUV = (VertexUvLoc >= 0); const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); +const HasLighting = HasNormal && !DepthPass; [layout(std140)] struct MaterialSettings @@ -96,8 +97,8 @@ external [tag("ViewerData")] viewerData: uniform[ViewerData], [tag("SkeletalData")] skeletalData: uniform[SkeletalData], [tag("LightData")] lightData: uniform[LightData], - [tag("ShadowMaps2D")] shadowMaps2D: array[sampler2D[f32], MaxLightCount], - [tag("ShadowMapsCube")] shadowMapsCube: array[samplerCube[f32], MaxLightCount] + [tag("ShadowMaps2D")] shadowMaps2D: array[depth_sampler2D[f32], MaxLightCount], + [tag("ShadowMapsCube")] shadowMapsCube: array[depth_sampler_cube[f32], MaxLightCount] } struct VertToFrag @@ -107,6 +108,7 @@ 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], } @@ -139,7 +141,7 @@ fn main(input: VertToFrag) -> FragOut discard; } - const if (HasNormal && !DepthPass) + const if (HasLighting) { let lightAmbient = vec3[f32](0.0, 0.0, 0.0); let lightDiffuse = vec3[f32](0.0, 0.0, 0.0); @@ -225,17 +227,41 @@ fn main(input: VertToFrag) -> FragOut let attenuationFactor = max(1.0 - dist * lightInvRadius, 0.0); attenuationFactor *= max((curAngle - lightOuterAngle) / innerMinusOuterAngle, 0.0); - lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb; - let lambert = max(dot(normal, -lightToPosNorm), 0.0); - lightDiffuse += attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor; - let reflection = reflect(lightToPosNorm, normal); let specFactor = max(dot(reflection, eyeVec), 0.0); specFactor = pow(specFactor, settings.Shininess); - lightSpecular += attenuationFactor * specFactor * light.color.rgb; + let shadowFactor = 0.0; + if (true) //< TODO: HasShadowMapping + { + let shadowTexSize = 1.0 / 512.0; //< FIXME + + let offsetArray = array[vec2[f32], 9]( + vec2[f32](-1.0, -1.0), + vec2[f32](-1.0, 0.0), + vec2[f32](-1.0, 1.0), + vec2[f32](0.0, -1.0), + vec2[f32](0.0, 0.0), + vec2[f32](0.0, 1.0), + vec2[f32](1.0, -1.0), + vec2[f32](1.0, 0.0), + vec2[f32](1.0, 1.0) + ); + + for offset in offsetArray + { + let shadowCoords = input.lightProjPos[i].xyz / input.lightProjPos[i].w; + shadowCoords.xy += offset * shadowTexSize; + shadowFactor += shadowMaps2D[i].SampleDepthComp(shadowCoords.xy, shadowCoords.z).r; + } + shadowFactor /= 9.0; + } + + lightAmbient += attenuationFactor * light.color.rgb * lightAmbientFactor * settings.AmbientColor.rgb; + lightDiffuse += shadowFactor * attenuationFactor * lambert * light.color.rgb * lightDiffuseFactor; + lightSpecular += shadowFactor * attenuationFactor * specFactor * light.color.rgb; } } @@ -382,5 +408,11 @@ 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; } diff --git a/src/Nazara/Graphics/ShaderReflection.cpp b/src/Nazara/Graphics/ShaderReflection.cpp index 599316d78..f7c995db7 100644 --- a/src/Nazara/Graphics/ShaderReflection.cpp +++ b/src/Nazara/Graphics/ShaderReflection.cpp @@ -73,16 +73,14 @@ namespace Nz else throw std::runtime_error("unexpected type " + ToString(varType)); - for (UInt32 i = 0; i < arraySize; ++i) - { - // TODO: Get more precise shader stage type - m_pipelineLayoutInfo.bindings.push_back({ - bindingSet, // setIndex - bindingIndex + i, // bindingIndex - bindingType, // type - nzsl::ShaderStageType_All // shaderStageFlags - }); - } + // TODO: Get more precise shader stage type + m_pipelineLayoutInfo.bindings.push_back({ + arraySize, // arraySize + bindingIndex, // bindingIndex + bindingSet, // setIndex + bindingType, // type + nzsl::ShaderStageType_All // shaderStageFlags + }); if (!externalVar.tag.empty() && externalBlock) { diff --git a/src/Nazara/Graphics/SpotLight.cpp b/src/Nazara/Graphics/SpotLight.cpp index e740307c3..55d16e8ac 100644 --- a/src/Nazara/Graphics/SpotLight.cpp +++ b/src/Nazara/Graphics/SpotLight.cpp @@ -30,6 +30,7 @@ namespace Nz AccessByOffset(data, lightOffset.lightMemberOffsets.parameter2) = Vector4f(m_direction.x, m_direction.y, m_direction.z, 0.f); AccessByOffset(data, lightOffset.lightMemberOffsets.parameter3) = Vector4f(m_innerAngleCos, m_outerAngleCos, 0.f, 0.f); AccessByOffset(data, lightOffset.lightMemberOffsets.shadowMappingFlag) = 0; + AccessByOffset(data, lightOffset.lightMemberOffsets.viewProjMatrix) = m_viewProjMatrix; } void SpotLight::UpdateTransform(const Vector3f& position, const Quaternionf& rotation, const Vector3f& /*scale*/) diff --git a/src/Nazara/Graphics/SubmeshRenderer.cpp b/src/Nazara/Graphics/SubmeshRenderer.cpp index d0093f335..a637ac962 100644 --- a/src/Nazara/Graphics/SubmeshRenderer.cpp +++ b/src/Nazara/Graphics/SubmeshRenderer.cpp @@ -54,9 +54,16 @@ namespace Nz currentShaderBinding = nullptr; }; - const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)]; + const auto& depthTexture2D = Graphics::Instance()->GetDefaultTextures().depthTextures[UnderlyingCast(ImageType::E2D)]; + const auto& depthTextureCube = Graphics::Instance()->GetDefaultTextures().depthTextures[UnderlyingCast(ImageType::Cubemap)]; + const auto& whiteTexture2D = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)]; + const auto& whiteTextureCube = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::Cubemap)]; const auto& defaultSampler = graphics->GetSamplerCache().Get({}); + TextureSamplerInfo samplerInfo; + samplerInfo.depthCompare = true; + const auto& shadowSampler = graphics->GetSamplerCache().Get(samplerInfo); + std::size_t oldDrawCallCount = data.drawCalls.size(); for (std::size_t i = 0; i < elementCount; ++i) @@ -122,6 +129,8 @@ namespace Nz assert(currentMaterialInstance); m_bindingCache.clear(); + m_textureBindingCache.clear(); + m_textureBindingCache.reserve(renderState.shadowMaps2D.size() + renderState.shadowMapsCube.size()); currentMaterialInstance->FillShaderBinding(m_bindingCache); const Material& material = *currentMaterialInstance->GetParentMaterial(); @@ -150,6 +159,50 @@ namespace Nz }; } + if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::Shadowmap2D); bindingIndex != Material::InvalidBindingIndex) + { + std::size_t textureBindingBaseIndex = m_textureBindingCache.size(); + + for (std::size_t j = 0; j < renderState.shadowMaps2D.size(); ++j) + { + const Texture* texture = renderState.shadowMaps2D[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::TextureBindings { + SafeCast(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex] + }; + } + + if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::ShadowmapCube); bindingIndex != Material::InvalidBindingIndex) + { + std::size_t textureBindingBaseIndex = m_textureBindingCache.size(); + + for (std::size_t j = 0; j < renderState.shadowMapsCube.size(); ++j) + { + const Texture* texture = renderState.shadowMapsCube[j]; + if (!texture) + texture = depthTextureCube.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::TextureBindings { + SafeCast(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex] + }; + } + if (UInt32 bindingIndex = material.GetEngineBindingIndex(EngineShaderBinding::SkeletalDataUbo); bindingIndex != Material::InvalidBindingIndex && currentSkeletonInstance) { const auto& skeletalBuffer = currentSkeletonInstance->GetSkeletalBuffer(); @@ -179,7 +232,7 @@ namespace Nz auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; bindingEntry.content = ShaderBinding::TextureBinding{ - whiteTexture.get(), defaultSampler.get() + whiteTexture2D.get(), defaultSampler.get() }; } diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp index f085fdd62..8f39b1611 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp @@ -21,10 +21,14 @@ namespace Nz unsigned int bindingIndex = 0; for (const auto& binding : m_layoutInfo.bindings) { - UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex); + for (UInt32 i = 0; i < binding.arraySize; ++i) + { + UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex + i); - m_bindingMapping[bindingKey] = bindingIndex++; - m_maxDescriptorCount = std::max(m_maxDescriptorCount, binding.bindingIndex + 1); + m_bindingMapping[bindingKey] = bindingIndex++; + } + + m_maxDescriptorCount = std::max(m_maxDescriptorCount, binding.bindingIndex + binding.arraySize); } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp index 2a73e9e5e..2588aba87 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp @@ -23,7 +23,18 @@ namespace Nz { using DescriptorType = std::decay_t; - auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding) { return binding.setIndex == setIndex && binding.bindingIndex == bindingIndex; }); + auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding) + { + if (binding.setIndex != setIndex) + return false; + + assert(binding.arraySize > 0); + if (bindingIndex < binding.bindingIndex || bindingIndex >= binding.bindingIndex + binding.arraySize) + return false; + + return true; + }); + if (bindingIt == layoutInfo.bindings.end()) throw std::runtime_error("invalid binding index"); @@ -88,26 +99,11 @@ namespace Nz storageDescriptor.buffer = 0; } else if constexpr (std::is_same_v) + HandleTextureBinding(binding.bindingIndex, arg); + else if constexpr (std::is_same_v) { - auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); - - if (const OpenGLTexture* glTexture = static_cast(arg.texture)) - { - textureDescriptor.texture = glTexture->GetTexture().GetObjectId(); - - if (const OpenGLTextureSampler* glSampler = static_cast(arg.sampler)) - textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId(); - else - textureDescriptor.sampler = 0; - - textureDescriptor.textureTarget = OpenGLTexture::ToTextureTarget(glTexture->GetType()); - } - else - { - textureDescriptor.sampler = 0; - textureDescriptor.texture = 0; - textureDescriptor.textureTarget = GL::TextureTarget::Target2D; - } + for (UInt32 i = 0; i < arg.arraySize; ++i) + HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]); } else if constexpr (std::is_same_v) { @@ -137,6 +133,29 @@ namespace Nz // No OpenGL object to name } + void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding) + { + auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex); + + if (const OpenGLTexture* glTexture = static_cast(textureBinding.texture)) + { + textureDescriptor.texture = glTexture->GetTexture().GetObjectId(); + + if (const OpenGLTextureSampler* glSampler = static_cast(textureBinding.sampler)) + textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId(); + else + textureDescriptor.sampler = 0; + + textureDescriptor.textureTarget = OpenGLTexture::ToTextureTarget(glTexture->GetType()); + } + else + { + textureDescriptor.sampler = 0; + textureDescriptor.texture = 0; + textureDescriptor.textureTarget = GL::TextureTarget::Target2D; + } + } + void OpenGLShaderBinding::Release() { m_owner.Release(*this); diff --git a/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp b/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp index f4d338a79..6f274382c 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLTextureSampler.cpp @@ -36,6 +36,12 @@ namespace Nz if (samplerInfo.anisotropyLevel > 1.f) sampler.SetParameterf(GL_TEXTURE_MAX_ANISOTROPY_EXT, samplerInfo.anisotropyLevel); + + if (samplerInfo.depthCompare) + { + sampler.SetParameteri(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + sampler.SetParameteri(GL_TEXTURE_COMPARE_FUNC, ToOpenGL(samplerInfo.depthComparison)); + } } void OpenGLTextureSampler::UpdateDebugName(std::string_view name) diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 078a8a6a1..ac5290dff 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -565,8 +565,13 @@ namespace Nz::GL glGetIntegerv(GL_VIEWPORT, res.data()); m_state.viewport = { res[0], res[1], res[2], res[3] }; - m_state.renderStates.depthCompare = RendererComparison::Less; //< OpenGL default depth mode is GL_LESS - m_state.renderStates.frontFace = FrontFace::CounterClockwise; //< OpenGL default front face is GL_CCW + // Set default OpenGL states + m_state.renderStates.depthBuffer = false; + m_state.renderStates.depthCompare = RendererComparison::Less; + m_state.renderStates.faceCulling = FaceCulling::None; + m_state.renderStates.frontFace = FrontFace::CounterClockwise; + m_state.renderStates.scissorTest = false; + m_state.renderStates.stencilTest = false; EnableVerticalSync(false); @@ -671,6 +676,18 @@ namespace Nz::GL if (!SetCurrentContext(this)) throw std::runtime_error("failed to activate context"); + // Depth bias + if (renderStates.depthBias) + { + if (!NumberEquals(m_state.renderStates.depthBiasConstantFactor, renderStates.depthBiasConstantFactor) || + !NumberEquals(m_state.renderStates.depthBiasSlopeFactor, renderStates.depthBiasSlopeFactor)) + { + glPolygonOffset(renderStates.depthBiasConstantFactor, renderStates.depthBiasSlopeFactor); + m_state.renderStates.depthBiasConstantFactor = renderStates.depthBiasConstantFactor; + m_state.renderStates.depthBiasSlopeFactor = renderStates.depthBiasSlopeFactor; + } + } + // Depth compare and depth write if (renderStates.depthBuffer) { @@ -687,14 +704,23 @@ namespace Nz::GL } } - // Face culling side - if (renderStates.faceCulling) + // Face culling + if (m_state.renderStates.faceCulling != renderStates.faceCulling) { - if (m_state.renderStates.cullingSide != renderStates.cullingSide) + bool wasEnabled = (m_state.renderStates.faceCulling != FaceCulling::None); + bool isEnabled = (renderStates.faceCulling != FaceCulling::None); + + if (isEnabled) { - glCullFace(ToOpenGL(renderStates.cullingSide)); - m_state.renderStates.cullingSide = renderStates.cullingSide; + if (!wasEnabled) + glEnable(GL_CULL_FACE); + + glCullFace(ToOpenGL(renderStates.faceCulling)); } + else if (wasEnabled) + glDisable(GL_CULL_FACE); + + m_state.renderStates.faceCulling = renderStates.faceCulling; } // Front face @@ -811,6 +837,18 @@ namespace Nz::GL m_state.renderStates.colorWriteMask = renderStates.colorWriteMask; } + // Depth bias + if (m_state.renderStates.depthBias != renderStates.depthBias) + { + // TODO: Handle line and points + if (renderStates.depthBias) + glEnable(GL_POLYGON_OFFSET_FILL); + else + glDisable(GL_POLYGON_OFFSET_FILL); + + m_state.renderStates.depthBias = renderStates.depthBias; + } + // Depth buffer if (m_state.renderStates.depthBuffer != renderStates.depthBuffer) { @@ -835,17 +873,6 @@ namespace Nz::GL m_state.renderStates.depthClamp = renderStates.depthClamp; } - // Face culling - if (m_state.renderStates.faceCulling != renderStates.faceCulling) - { - if (renderStates.faceCulling) - glEnable(GL_CULL_FACE); - else - glDisable(GL_CULL_FACE); - - m_state.renderStates.faceCulling = renderStates.faceCulling; - } - // Scissor test if (m_state.renderStates.scissorTest != renderStates.scissorTest) { diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 22d2427ac..87442cb5a 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -39,7 +39,7 @@ namespace Nz RenderPipelineLayoutInfo layoutInfo; layoutInfo.bindings.assign({ { - 0, 0, + 1, 0, 0, ShaderBindingType::UniformBuffer, nzsl::ShaderStageType::Vertex } diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index 5f7c31562..2b53fe041 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -133,8 +133,8 @@ namespace Nz std::string appName = parameters.GetStringParameter("VkAppInfo_OverrideApplicationName").GetValueOr("Another application made with Nazara Engine"); std::string engineName = parameters.GetStringParameter("VkAppInfo_OverrideEngineName").GetValueOr("Nazara Engine - Vulkan Renderer"); - UInt32 appVersion = parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)); - UInt32 engineVersion = parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0)); + UInt32 appVersion = SafeCast(parameters.GetIntegerParameter("VkAppInfo_OverrideApplicationVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0))); + UInt32 engineVersion = SafeCast(parameters.GetIntegerParameter("VkAppInfo_OverrideEngineVersion").GetValueOr(VK_MAKE_API_VERSION(0, 1, 0, 0))); if (auto result = parameters.GetIntegerParameter("VkAppInfo_OverrideAPIVersion")) targetApiVersion = SafeCast(result.GetValue()); @@ -160,7 +160,7 @@ namespace Nz targetApiVersion }; - VkInstanceCreateFlags createFlags = parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags").GetValueOr(0); + VkInstanceCreateFlags createFlags = SafeCast(parameters.GetIntegerParameter("VkInstanceInfo_OverrideCreateFlags").GetValueOr(0)); std::vector enabledLayers; diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp index 91835fcb4..6fe7e587a 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp @@ -159,7 +159,10 @@ namespace Nz { VkPipelineRasterizationStateCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - createInfo.cullMode = (pipelineInfo.faceCulling) ? ToVulkan(pipelineInfo.cullingSide) : VK_CULL_MODE_NONE; + createInfo.cullMode = ToVulkan(pipelineInfo.faceCulling); + createInfo.depthBiasEnable = pipelineInfo.depthBias; + createInfo.depthBiasConstantFactor = pipelineInfo.depthBiasConstantFactor; + createInfo.depthBiasSlopeFactor = pipelineInfo.depthBiasSlopeFactor; createInfo.depthClampEnable = pipelineInfo.depthClamp; createInfo.frontFace = ToVulkan(pipelineInfo.frontFace); createInfo.lineWidth = pipelineInfo.lineWidth; diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp index 82b5d4030..5735f302f 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp @@ -69,7 +69,7 @@ namespace Nz VkDescriptorSetLayoutBinding& layoutBinding = descriptorSetLayoutInfo.bindings.emplace_back(); layoutBinding.binding = bindingInfo.bindingIndex; - layoutBinding.descriptorCount = 1U; + layoutBinding.descriptorCount = bindingInfo.arraySize; layoutBinding.descriptorType = ToVulkan(bindingInfo.type); layoutBinding.pImmutableSamplers = nullptr; layoutBinding.stageFlags = ToVulkan(bindingInfo.shaderStageFlags); diff --git a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp index ef03bbe08..f1361d78a 100644 --- a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp +++ b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp @@ -15,8 +15,34 @@ namespace Nz { void VulkanShaderBinding::Update(const Binding* bindings, std::size_t bindingCount) { - StackVector bufferBinding = NazaraStackVector(VkDescriptorBufferInfo, bindingCount); - StackVector imageBinding = NazaraStackVector(VkDescriptorImageInfo, bindingCount); + std::size_t bufferBindingCount = 0; + std::size_t imageBindingCount = 0; + for (std::size_t i = 0; i < bindingCount; ++i) + { + const Binding& binding = bindings[i]; + + std::visit([&](auto&& arg) + { + using T = std::decay_t; + + if constexpr (std::is_same_v || std::is_same_v) + bufferBindingCount++; + else if constexpr (std::is_same_v) + imageBindingCount++; + else if constexpr (std::is_same_v) + imageBindingCount += arg.arraySize; + else + static_assert(AlwaysFalse(), "non-exhaustive visitor"); + + }, binding.content); + } + + NazaraAssert(bufferBindingCount < 128, "too many concurrent buffer update"); + NazaraAssert(imageBindingCount < 128, "too many concurrent image binding update"); + NazaraAssert(bindingCount < 128, "too many binding update"); + + StackVector bufferBinding = NazaraStackVector(VkDescriptorBufferInfo, bufferBindingCount); + StackVector imageBinding = NazaraStackVector(VkDescriptorImageInfo, imageBindingCount); StackVector writeOps = NazaraStackVector(VkWriteDescriptorSet, bindingCount); for (std::size_t i = 0; i < bindingCount; ++i) @@ -34,7 +60,7 @@ namespace Nz if constexpr (std::is_same_v) { - VulkanBuffer* vkBuffer = static_cast(arg.buffer); + VulkanBuffer* vkBuffer = SafeCast(arg.buffer); VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back(); bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE; @@ -47,8 +73,8 @@ namespace Nz } else if constexpr (std::is_same_v) { - const VulkanTexture* vkTexture = static_cast(arg.texture); - const VulkanTextureSampler* vkSampler = static_cast(arg.sampler); + const VulkanTexture* vkTexture = SafeCast(arg.texture); + const VulkanTextureSampler* vkSampler = SafeCast(arg.sampler); VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back(); imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -59,6 +85,23 @@ namespace Nz writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeOp.pImageInfo = &imageInfo; } + else if constexpr (std::is_same_v) + { + for (UInt32 i = 0; i < arg.arraySize; ++i) + { + const VulkanTexture* vkTexture = SafeCast(arg.textureBindings[i].texture); + const VulkanTextureSampler* vkSampler = SafeCast(arg.textureBindings[i].sampler); + + VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back(); + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = (vkTexture) ? vkTexture->GetImageView() : VK_NULL_HANDLE; + imageInfo.sampler = (vkSampler) ? vkSampler->GetSampler() : VK_NULL_HANDLE; + } + + writeOp.descriptorCount = arg.arraySize; + writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeOp.pImageInfo = &imageBinding[imageBinding.size() - arg.arraySize]; + } else if constexpr (std::is_same_v) { VulkanBuffer* vkBuffer = static_cast(arg.buffer); diff --git a/src/Nazara/VulkanRenderer/VulkanTexture.cpp b/src/Nazara/VulkanRenderer/VulkanTexture.cpp index ca4179556..69f8a5ade 100644 --- a/src/Nazara/VulkanRenderer/VulkanTexture.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTexture.cpp @@ -293,15 +293,19 @@ namespace Nz if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) return false; + VkImageAspectFlagBits aspect = VK_IMAGE_ASPECT_COLOR_BIT; + if (PixelFormatInfo::GetContent(m_params.pixelFormat) == PixelFormatContent::Depth) + aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageSubresourceLayers subresourceLayers = { //< FIXME - VK_IMAGE_ASPECT_COLOR_BIT, + aspect, level, //< mipLevel 0, //< baseArrayLayer UInt32((m_params.type == ImageType::Cubemap) ? 6 : 1) //< layerCount }; VkImageSubresourceRange subresourceRange = { //< FIXME - VK_IMAGE_ASPECT_COLOR_BIT, + aspect, 0, //< baseMipLevel 1, //< levelCount subresourceLayers.baseArrayLayer, //< baseArrayLayer diff --git a/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp index cef51a272..809841890 100644 --- a/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp @@ -22,6 +22,8 @@ namespace Nz createInfo.addressModeW = ToVulkan(samplerInfo.wrapModeW); createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode); + createInfo.compareEnable = samplerInfo.depthCompare; + createInfo.compareOp = ToVulkan(samplerInfo.depthComparison); if (samplerInfo.anisotropyLevel > 1.f) { diff --git a/test.txt b/test.txt new file mode 100644 index 000000000..fd7b7efe1 --- /dev/null +++ b/test.txt @@ -0,0 +1,13 @@ +checking for platform ... linux +checking for architecture ... x86_64 +checking for gcc ... /usr/bin/gcc +checkinfo: cannot runv(dmd --version), No such file or directory +checking for dmd ... no +checkinfo: cannot runv(ldc2 --version), No such file or directory +checking for ldc2 ... no +checkinfo: cannot runv(gdc --version), No such file or directory +checking for gdc ... no +checkinfo: cannot runv(zig version), No such file or directory +checking for zig ... no +checkinfo: cannot runv(zig version), No such file or directory +checking for zig ... no