diff --git a/bin/resources/bloom_bright.nzsl b/bin/resources/bloom_bright.nzsl new file mode 100644 index 000000000..c9804c7c2 --- /dev/null +++ b/bin/resources/bloom_bright.nzsl @@ -0,0 +1,69 @@ +[layout(std140)] +struct ViewerData +{ + projectionMatrix: mat4, + invProjectionMatrix: mat4, + viewMatrix: mat4, + invViewMatrix: mat4, + viewProjMatrix: mat4, + invViewProjMatrix: mat4, + renderTargetSize: vec2, + invRenderTargetSize: vec2, + eyePosition: vec3 +} + +external +{ + [binding(0)] colorTexture: sampler2D, + [binding(1)] viewerData: uniform +} + +struct FragIn +{ + [builtin(fragcoord)] fragcoord: vec4 +} + +struct FragOut +{ + [location(0)] color: vec4 +} + +struct VertIn +{ + [location(0)] pos: vec3 +} + +struct VertOut +{ + [builtin(position)] position: vec4 +} + +[entry(frag)] +fn main(input: FragIn) -> FragOut +{ + let BrightLuminance = 0.6; + let BrightMiddleGrey = 0.5; + let BrightThreshold = 0.7; + + let fragcoord = input.fragcoord.xy * viewerData.invRenderTargetSize * 10.0; + + let color = colorTexture.Sample(fragcoord).rgb; + color = color * (BrightMiddleGrey/BrightLuminance); + color = color * (vec3(1.0, 1.0, 1.0) + (color / (BrightThreshold*BrightThreshold))); + color = color - vec3(0.5, 0.5, 0.5); + color = color / (vec3(1.0, 1.0, 1.0) + color); + + let output: FragOut; + output.color = vec4(color, 1.0); + + return output; +} + +[entry(vert)] +fn main(input: VertIn) -> VertOut +{ + let output: VertOut; + output.position = vec4(input.pos, 1.0); + + return output; +} diff --git a/bin/resources/bloom_final.nzsl b/bin/resources/bloom_final.nzsl new file mode 100644 index 000000000..506917ffe --- /dev/null +++ b/bin/resources/bloom_final.nzsl @@ -0,0 +1,60 @@ +[layout(std140)] +struct ViewerData +{ + projectionMatrix: mat4, + invProjectionMatrix: mat4, + viewMatrix: mat4, + invViewMatrix: mat4, + viewProjMatrix: mat4, + invViewProjMatrix: mat4, + renderTargetSize: vec2, + invRenderTargetSize: vec2, + eyePosition: vec3 +} + +external +{ + [binding(0)] colorTexture: sampler2D, + [binding(1)] bloomTexture: sampler2D, + [binding(2)] viewerData: uniform +} + +struct FragIn +{ + [builtin(fragcoord)] fragcoord: vec4 +} + +struct FragOut +{ + [location(0)] color: vec4 +} + +struct VertIn +{ + [location(0)] pos: vec3 +} + +struct VertOut +{ + [builtin(position)] position: vec4 +} + +[entry(frag)] +fn main(input: FragIn) -> FragOut +{ + let fragcoord = input.fragcoord.xy * viewerData.invRenderTargetSize; + + let output: FragOut; + output.color = colorTexture.Sample(fragcoord) + bloomTexture.Sample(fragcoord); + + return output; +} + +[entry(vert)] +fn main(input: VertIn) -> VertOut +{ + let output: VertOut; + output.position = vec4(input.pos, 1.0); + + return output; +} diff --git a/bin/resources/gaussian_blur.nzsl b/bin/resources/gaussian_blur.nzsl new file mode 100644 index 000000000..24907fb7d --- /dev/null +++ b/bin/resources/gaussian_blur.nzsl @@ -0,0 +1,78 @@ +[layout(std140)] +struct ViewerData +{ + projectionMatrix: mat4, + invProjectionMatrix: mat4, + viewMatrix: mat4, + invViewMatrix: mat4, + viewProjMatrix: mat4, + invViewProjMatrix: mat4, + renderTargetSize: vec2, + invRenderTargetSize: vec2, + eyePosition: vec3 +} + +external +{ + [binding(0)] colorTexture: sampler2D, + [binding(1)] viewerData: uniform +} + +struct FragIn +{ + [builtin(fragcoord)] fragcoord: vec4 +} + +struct FragOut +{ + [location(0)] color: vec4 +} + +struct VertIn +{ + [location(0)] pos: vec3 +} + +struct VertOut +{ + [builtin(position)] position: vec4 +} + +[entry(frag)] +fn main(input: FragIn) -> FragOut +{ + let invTargetSize = viewerData.invRenderTargetSize * 10.0; + let fragcoord = input.fragcoord.xy * invTargetSize; + + let color = colorTexture.Sample(fragcoord).rgb * 0.2270270270; + + let filter = vec2(1.0, 0.0); + + color = color + colorTexture.Sample(fragcoord + filter * 1.3846153846 * invTargetSize).rgb * 0.3162162162; + color = color + colorTexture.Sample(fragcoord - filter * 1.3846153846 * invTargetSize).rgb * 0.3162162162; + + color = color + colorTexture.Sample(fragcoord + filter * 3.2307692308 * invTargetSize).rgb * 0.0702702703; + color = color + colorTexture.Sample(fragcoord - filter * 3.2307692308 * invTargetSize).rgb * 0.0702702703; + + filter = vec2(0.0, 1.0); + + color = color + colorTexture.Sample(fragcoord + filter * 1.3846153846 * invTargetSize).rgb * 0.3162162162; + color = color + colorTexture.Sample(fragcoord - filter * 1.3846153846 * invTargetSize).rgb * 0.3162162162; + + color = color + colorTexture.Sample(fragcoord + filter * 3.2307692308 * invTargetSize).rgb * 0.0702702703; + color = color + colorTexture.Sample(fragcoord - filter * 3.2307692308 * invTargetSize).rgb * 0.0702702703; + + let output: FragOut; + output.color = vec4(color, 1.0); + + return output; +} + +[entry(vert)] +fn main(input: VertIn) -> VertOut +{ + let output: VertOut; + output.position = vec4(input.pos, 1.0); + + return output; +} diff --git a/bin/resources/skybox.nzsl b/bin/resources/skybox.nzsl index 9d5b8660c..159323c05 100644 --- a/bin/resources/skybox.nzsl +++ b/bin/resources/skybox.nzsl @@ -55,6 +55,7 @@ fn main(input: VertIn) -> VertOut { // Set translation part to zero let rotationMat = viewerData.viewMatrix; + // rotationMat[3].xyz = vec3(0.0, 0.0, 0.0); // Requires SPIRV generator to handle swizzle for store expressions rotationMat[3][0] = 0.0; rotationMat[3][1] = 0.0; rotationMat[3][2] = 0.0; diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 4756f4be8..16dcc7929 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -351,20 +351,88 @@ int main() } - const std::shared_ptr& vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_UV); + const std::shared_ptr& fullscreenVertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_UV); unsigned int offscreenWidth = window.GetSize().x; unsigned int offscreenHeight = window.GetSize().y; + // Bloom data + + Nz::RenderPipelineLayoutInfo bloomPipelineLayoutInfo; + bloomPipelineLayoutInfo.bindings.push_back({ + Nz::ShaderBindingType::Texture, + Nz::ShaderStageType::Fragment, + 0 + }); + + bloomPipelineLayoutInfo.bindings.push_back({ + Nz::ShaderBindingType::UniformBuffer, + Nz::ShaderStageType::Fragment, + 1 + }); + + Nz::RenderPipelineLayoutInfo bloomBlendPipelineLayoutInfo; + bloomBlendPipelineLayoutInfo.bindings.push_back({ + Nz::ShaderBindingType::Texture, + Nz::ShaderStageType::Fragment, + 0 + }); + + bloomBlendPipelineLayoutInfo.bindings.push_back({ + Nz::ShaderBindingType::Texture, + Nz::ShaderStageType::Fragment, + 1 + }); + + bloomBlendPipelineLayoutInfo.bindings.push_back({ + Nz::ShaderBindingType::UniformBuffer, + Nz::ShaderStageType::Fragment, + 2 + }); + + Nz::RenderPipelineInfo bloomPipelineInfo; + bloomPipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleList; + bloomPipelineInfo.pipelineLayout = device->InstantiateRenderPipelineLayout(bloomPipelineLayoutInfo); + bloomPipelineInfo.vertexBuffers.push_back({ + 0, + fullscreenVertexDeclaration + }); + + bloomPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "bloom_bright.nzsl", {})); + + std::shared_ptr bloomBrightShaderBinding = bloomPipelineInfo.pipelineLayout->AllocateShaderBinding(); + std::shared_ptr gaussianBlurShaderBinding = bloomPipelineInfo.pipelineLayout->AllocateShaderBinding(); + + std::shared_ptr bloomBrightPipeline = device->InstantiateRenderPipeline(bloomPipelineInfo); + + bloomPipelineInfo.shaderModules.clear(); + bloomPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "gaussian_blur.nzsl", {})); + + std::shared_ptr gaussianBlurPipeline = device->InstantiateRenderPipeline(bloomPipelineInfo); + + Nz::RenderPipelineInfo bloomBlendPipelineInfo; + bloomBlendPipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleList; + bloomBlendPipelineInfo.pipelineLayout = device->InstantiateRenderPipelineLayout(bloomBlendPipelineLayoutInfo); + bloomBlendPipelineInfo.vertexBuffers.push_back({ + 0, + fullscreenVertexDeclaration + }); + + bloomBlendPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment | Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraShader, resourceDir / "bloom_final.nzsl", {})); + + std::shared_ptr bloomBlendPipeline = device->InstantiateRenderPipeline(bloomBlendPipelineInfo); + + std::shared_ptr bloomBlendShaderBinding = bloomBlendPipelineInfo.pipelineLayout->AllocateShaderBinding(); + // Fullscreen data Nz::RenderPipelineInfo fullscreenPipelineInfo; - fullscreenPipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleStrip; + fullscreenPipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleList; fullscreenPipelineInfo.pipelineLayout = device->InstantiateRenderPipelineLayout(fullscreenPipelineLayoutInfo); fullscreenPipelineInfo.vertexBuffers.push_back({ 0, - vertexDeclaration + fullscreenVertexDeclaration }); fullscreenPipelineInfo.shaderModules.push_back(device->InstantiateShaderModule(Nz::ShaderStageType::Fragment, Nz::ShaderLanguage::NazaraBinary, resourceDir / "fullscreen.frag.shader", {})); @@ -461,21 +529,15 @@ int main() } };*/ - std::shared_ptr vertexBuffer = device->InstantiateBuffer(Nz::BufferType::Vertex); - if (!vertexBuffer->Initialize(vertexDeclaration->GetStride() * vertexData.size(), Nz::BufferUsage::DeviceLocal)) + std::shared_ptr fullscreenVertexBuffer = device->InstantiateBuffer(Nz::BufferType::Vertex); + if (!fullscreenVertexBuffer->Initialize(fullscreenVertexDeclaration->GetStride() * vertexData.size(), Nz::BufferUsage::DeviceLocal)) return __LINE__; - if (!vertexBuffer->Fill(vertexData.data(), 0, vertexBuffer->GetSize())) + if (!fullscreenVertexBuffer->Fill(vertexData.data(), 0, fullscreenVertexBuffer->GetSize())) return __LINE__; std::shared_ptr finalBlitBinding = fullscreenPipelineInfo.pipelineLayout->AllocateShaderBinding(); - std::size_t colorTexture; - std::size_t normalTexture; - std::size_t positionTexture; - std::size_t depthBuffer; - std::size_t backbuffer; - bool viewerUboUpdate = true; bool lightUpdate = true; @@ -499,9 +561,19 @@ int main() } }); + bool bloomEnabled = true; bool forwardEnabled = true; bool lightAnimation = true; + std::size_t colorTexture; + std::size_t normalTexture; + std::size_t positionTexture; + std::size_t depthBuffer; + std::size_t backbuffer; + std::size_t bloomTextureA; + std::size_t bloomTextureB; + std::size_t lightOutput; + Nz::BakedFrameGraph bakedGraph = [&] { Nz::FrameGraph graph; @@ -526,11 +598,30 @@ int main() Nz::PixelFormat::Depth24Stencil8 }); + lightOutput = graph.AddAttachment({ + "Light output", + Nz::PixelFormat::RGBA8 + }); + backbuffer = graph.AddAttachment({ "Backbuffer", Nz::PixelFormat::RGBA8 }); + bloomTextureA = graph.AddAttachment({ + "Bloom texture A", + Nz::PixelFormat::RGBA8, + 10'000, + 10'000 + }); + + bloomTextureB = graph.AddAttachment({ + "Bloom texture B", + Nz::PixelFormat::RGBA8, + 10'000, + 10'000 + }); + Nz::FramePass& gbufferPass = graph.AddPass("GBuffer"); std::size_t geometryAlbedo = gbufferPass.AddOutput(colorTexture); @@ -608,7 +699,7 @@ int main() lightingPass.AddInput(colorTexture); lightingPass.AddInput(normalTexture); lightingPass.AddInput(positionTexture); - lightingPass.SetClearColor(lightingPass.AddOutput(backbuffer), Nz::Color::Black); + lightingPass.SetClearColor(lightingPass.AddOutput(lightOutput), Nz::Color::Black); lightingPass.SetDepthStencilInput(depthBuffer); lightingPass.SetDepthStencilOutput(depthBuffer); @@ -631,11 +722,73 @@ int main() return (forwardEnabled) ? Nz::FramePassExecution::Execute : Nz::FramePassExecution::Skip; }); - forwardPass.AddInput(backbuffer); - forwardPass.AddOutput(backbuffer); + forwardPass.AddInput(lightOutput); + forwardPass.AddOutput(lightOutput); forwardPass.SetDepthStencilInput(depthBuffer); forwardPass.SetDepthStencilOutput(depthBuffer); + + Nz::FramePass& bloomBrightPass = graph.AddPass("Bloom pass - extract bright pixels"); + bloomBrightPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder) + { + builder.SetScissor(Nz::Recti{ 0, 0, int(offscreenWidth) / 10, int(offscreenHeight) / 10 }); + builder.SetViewport(Nz::Recti{ 0, 0, int(offscreenWidth) / 10, int(offscreenHeight) / 10 }); + + builder.BindShaderBinding(*bloomBrightShaderBinding); + builder.BindPipeline(*bloomBrightPipeline); + builder.BindVertexBuffer(0, fullscreenVertexBuffer.get()); + + builder.Draw(3); + }); + bloomBrightPass.SetExecutionCallback([&] + { + return (bloomEnabled) ? Nz::FramePassExecution::Execute : Nz::FramePassExecution::Skip; + }); + + bloomBrightPass.AddInput(lightOutput); + bloomBrightPass.AddOutput(bloomTextureA); + + Nz::FramePass& bloomBlurPass = graph.AddPass("Bloom pass - gaussian blur"); + bloomBlurPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder) + { + builder.SetScissor(Nz::Recti{ 0, 0, int(offscreenWidth) / 10, int(offscreenHeight) / 10 }); + builder.SetViewport(Nz::Recti{ 0, 0, int(offscreenWidth) / 10, int(offscreenHeight) / 10 }); + + builder.BindShaderBinding(*gaussianBlurShaderBinding); + builder.BindPipeline(*gaussianBlurPipeline); + builder.BindVertexBuffer(0, fullscreenVertexBuffer.get()); + + builder.Draw(3); + }); + bloomBlurPass.SetExecutionCallback([&] + { + return (bloomEnabled) ? Nz::FramePassExecution::Execute : Nz::FramePassExecution::Skip; + }); + + bloomBlurPass.AddInput(bloomTextureA); + bloomBlurPass.AddOutput(bloomTextureB); + + Nz::FramePass& bloomBlendPass = graph.AddPass("Bloom pass - blend"); + bloomBlendPass.SetCommandCallback([&](Nz::CommandBufferBuilder& builder) + { + builder.SetScissor(Nz::Recti{ 0, 0, int(offscreenWidth), int(offscreenHeight) }); + builder.SetViewport(Nz::Recti{ 0, 0, int(offscreenWidth), int(offscreenHeight) }); + + builder.BindShaderBinding(*bloomBlendShaderBinding); + builder.BindPipeline(*bloomBlendPipeline); + builder.BindVertexBuffer(0, fullscreenVertexBuffer.get()); + + builder.Draw(3); + }); + bloomBlendPass.SetExecutionCallback([&] + { + return (bloomEnabled) ? Nz::FramePassExecution::Execute : Nz::FramePassExecution::Skip; + }); + + bloomBlendPass.AddInput(lightOutput); + bloomBlendPass.AddInput(bloomTextureB); + bloomBlendPass.AddOutput(backbuffer); + graph.SetBackbufferOutput(backbuffer); return graph.Bake(); @@ -688,6 +841,64 @@ int main() lightingShaderBindings.emplace_back(std::move(lightingShaderBinding)); } + bloomBrightShaderBinding->Update({ + { + 0, + Nz::ShaderBinding::TextureBinding { + bakedGraph.GetAttachmentTexture(lightOutput).get(), + textureSampler.get() + } + }, + { + 1, + Nz::ShaderBinding::UniformBufferBinding { + viewerDataUBO.get(), + 0, viewerDataUBO->GetSize() + } + } + }); + + gaussianBlurShaderBinding->Update({ + { + 0, + Nz::ShaderBinding::TextureBinding { + bakedGraph.GetAttachmentTexture(bloomTextureA).get(), + textureSampler.get() + } + }, + { + 1, + Nz::ShaderBinding::UniformBufferBinding { + viewerDataUBO.get(), + 0, viewerDataUBO->GetSize() + } + } + }); + + bloomBlendShaderBinding->Update({ + { + 0, + Nz::ShaderBinding::TextureBinding { + bakedGraph.GetAttachmentTexture(lightOutput).get(), + textureSampler.get() + } + }, + { + 1, + Nz::ShaderBinding::TextureBinding { + bakedGraph.GetAttachmentTexture(bloomTextureB).get(), + textureSampler.get() + } + }, + { + 2, + Nz::ShaderBinding::UniformBufferBinding { + viewerDataUBO.get(), + 0, viewerDataUBO->GetSize() + } + } + }); + finalBlitBinding->Update({ { 0, @@ -719,7 +930,7 @@ int main() builder.BindShaderBinding(*finalBlitBinding); builder.BindPipeline(*fullscreenPipeline); - builder.BindVertexBuffer(0, vertexBuffer.get()); + builder.BindVertexBuffer(0, fullscreenVertexBuffer.get()); builder.Draw(3); } builder.EndRenderPass(); diff --git a/include/Nazara/Graphics/FramePassAttachment.hpp b/include/Nazara/Graphics/FramePassAttachment.hpp index 09f0a2cb6..f77afe314 100644 --- a/include/Nazara/Graphics/FramePassAttachment.hpp +++ b/include/Nazara/Graphics/FramePassAttachment.hpp @@ -18,6 +18,8 @@ namespace Nz { std::string name; PixelFormat format; + unsigned int width = 100'000; + unsigned int height = 100'000; }; } diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 11328ec48..cd9874e6c 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -172,8 +172,8 @@ namespace Nz TextureData& data = m_pending.textures.emplace_back(); data.format = m_attachments[attachmentIndex].format; - data.width = 100'000; - data.height = 100'000; + data.width = m_attachments[attachmentIndex].width; + data.height = m_attachments[attachmentIndex].height; return textureId; } diff --git a/src/Nazara/Shader/SpirvAstVisitor.cpp b/src/Nazara/Shader/SpirvAstVisitor.cpp index f32035280..961435317 100644 --- a/src/Nazara/Shader/SpirvAstVisitor.cpp +++ b/src/Nazara/Shader/SpirvAstVisitor.cpp @@ -338,7 +338,7 @@ namespace Nz rightOperand = rightAsVec; } - else if (!IsPrimitiveType(leftType) || !IsPrimitiveType(rightType)) + else if (leftType != rightType) throw std::runtime_error("unexpected division operands"); } diff --git a/src/ShaderNode/DataModels/VecValue.inl b/src/ShaderNode/DataModels/VecValue.inl index 90b6ca375..e65924845 100644 --- a/src/ShaderNode/DataModels/VecValue.inl +++ b/src/ShaderNode/DataModels/VecValue.inl @@ -116,7 +116,7 @@ void VecValue::BuildNodeEdition(QFormLayout* layout) connect(spinbox, qOverload(&QDoubleSpinBox::valueChanged), [=](double) { - m_value[i] = spinbox->value(); + m_value[i] = float(spinbox->value()); Q_EMIT dataUpdated(0); UpdatePreview();