From 2da564d5cfc66ee97a5fd8098df6cbe8f5972363 Mon Sep 17 00:00:00 2001 From: Alexander Gauggel <agauggel@uni-koblenz.de> Date: Tue, 10 Aug 2021 17:19:42 +0200 Subject: [PATCH] [#106] Naive motion blur implementation --- .../resources/shaders/motionBlurDummy.comp | 38 ++++++++++ .../resources/shaders/motionVector.inc | 9 +++ .../resources/shaders/prepass.frag | 14 ++-- .../resources/shaders/skyPrepass.frag | 14 ++++ .../resources/shaders/skyPrepass.vert | 22 ++++++ projects/indirect_dispatch/src/App.cpp | 70 ++++++++++++++----- projects/indirect_dispatch/src/App.hpp | 8 ++- projects/indirect_dispatch/src/AppSetup.cpp | 30 ++++++++ projects/indirect_dispatch/src/AppSetup.hpp | 8 ++- 9 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp create mode 100644 projects/indirect_dispatch/resources/shaders/motionVector.inc create mode 100644 projects/indirect_dispatch/resources/shaders/skyPrepass.frag create mode 100644 projects/indirect_dispatch/resources/shaders/skyPrepass.vert diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp b/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp new file mode 100644 index 00000000..a3a21557 --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp @@ -0,0 +1,38 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform texture2D inMotion; +layout(set=0, binding=2) uniform sampler textureSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))) + return; + + ivec2 textureRes = textureSize(sampler2D(inColor, textureSampler), 0); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = vec2(coord) / textureRes; + + vec2 motion = texture(sampler2D(inMotion, textureSampler), uv).rg; + float blurFactor = 5; + motion *= blurFactor; + + vec3 color = vec3(0); + const int sampleCount = 16; + + vec2 uvStart = uv - motion; + vec2 uvEnd = uv + motion; + + for(int i = 0; i < sampleCount; i++){ + vec2 sampleUV = mix(uvStart, uvEnd, i / float(sampleCount - 1)); + color += texture(sampler2D(inColor, textureSampler), sampleUV).rgb; + } + + color /= sampleCount; + + imageStore(outImage, coord, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionVector.inc b/projects/indirect_dispatch/resources/shaders/motionVector.inc new file mode 100644 index 00000000..498478cb --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionVector.inc @@ -0,0 +1,9 @@ +vec2 computeMotionVector(vec4 NDC, vec4 NDCPrevious){ + vec2 ndc = NDC.xy / NDC.w; + vec2 ndcPrevious = NDCPrevious.xy / NDCPrevious.w; + + vec2 uv = ndc * 0.5 + 0.5; + vec2 uvPrevious = ndcPrevious * 0.5 + 0.5; + + return uvPrevious - uv; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/prepass.frag b/projects/indirect_dispatch/resources/shaders/prepass.frag index e9030883..ccfc84d9 100644 --- a/projects/indirect_dispatch/resources/shaders/prepass.frag +++ b/projects/indirect_dispatch/resources/shaders/prepass.frag @@ -1,5 +1,8 @@ #version 450 -#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "motionVector.inc" layout(location = 0) in vec4 passNDC; layout(location = 1) in vec4 passNDCPrevious; @@ -7,12 +10,5 @@ layout(location = 1) in vec4 passNDCPrevious; layout(location = 0) out vec2 outMotion; void main() { - - vec2 ndc = passNDC.xy / passNDC.w; - vec2 ndcPrevious = passNDCPrevious.xy / passNDCPrevious.w; - - vec2 uv = ndc * 0.5 + 0.5; - vec2 uvPrevious = ndcPrevious * 0.5 + 0.5; - - outMotion = uvPrevious - uv; + outMotion = computeMotionVector(passNDC, passNDCPrevious); } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/skyPrepass.frag b/projects/indirect_dispatch/resources/shaders/skyPrepass.frag new file mode 100644 index 00000000..64ec4f18 --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/skyPrepass.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "motionVector.inc" + +layout(location = 0) out vec2 outMotion; + +layout(location = 0) in vec4 passNDC; +layout(location = 1) in vec4 passNDCPrevious; + +void main() { + outMotion = computeMotionVector(passNDC, passNDCPrevious); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/skyPrepass.vert b/projects/indirect_dispatch/resources/shaders/skyPrepass.vert new file mode 100644 index 00000000..31b9016a --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/skyPrepass.vert @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout( push_constant ) uniform constants{ + mat4 viewProjection; + mat4 viewProjectionPrevious; +}; + +layout(location = 0) out vec4 passNDC; +layout(location = 1) out vec4 passNDCPrevious; + +void main() { + gl_Position = viewProjection * vec4(inPosition, 0.0); + gl_Position.w = gl_Position.z; + + passNDC = gl_Position; + + passNDCPrevious = viewProjectionPrevious * vec4(inPosition, 0.0); + passNDCPrevious.w = passNDCPrevious.z; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp index c74ffe2b..8ab6bb88 100644 --- a/projects/indirect_dispatch/src/App.cpp +++ b/projects/indirect_dispatch/src/App.cpp @@ -23,18 +23,24 @@ App::App() : bool App::initialize() { - if (!loadMeshPass(m_core, &m_meshPassHandles)) + if (!loadMeshPass(m_core, &m_meshPass)) return false; - if (!loadSkyPass(m_core, &m_skyPassHandles)) + if (!loadSkyPass(m_core, &m_skyPass)) return false; - if (!loadPrePass(m_core, &m_prePassHandles)) + if (!loadPrePass(m_core, &m_prePass)) false; + if (!loadSkyPrePass(m_core, &m_skyPrePass)) + return false; + if (!loadComputePass(m_core, "resources/shaders/gammaCorrection.comp", &m_gammaCorrectionPass)) return false; + if(!loadComputePass(m_core, "resources/shaders/motionBlurDummy.comp", &m_motionBlurDummyPass)) + return false; + if (!loadMesh(m_core, "resources/models/sphere.gltf", & m_sphereMesh)) return false; @@ -106,8 +112,17 @@ void App::run() { m_core.recordDrawcallsToCmdStream( cmdStream, - m_prePassHandles.renderPass, - m_prePassHandles.pipeline, + m_prePass.renderPass, + m_prePass.pipeline, + prepassPushConstants, + { sphereDrawcall }, + prepassRenderTargets); + + // sky prepass + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_skyPrePass.renderPass, + m_skyPrePass.pipeline, prepassPushConstants, { sphereDrawcall }, prepassRenderTargets); @@ -122,8 +137,8 @@ void App::run() { m_core.recordDrawcallsToCmdStream( cmdStream, - m_meshPassHandles.renderPass, - m_meshPassHandles.pipeline, + m_meshPass.renderPass, + m_meshPass.pipeline, meshPushConstants, { sphereDrawcall }, renderTargets); @@ -134,14 +149,42 @@ void App::run() { m_core.recordDrawcallsToCmdStream( cmdStream, - m_skyPassHandles.renderPass, - m_skyPassHandles.pipeline, + m_skyPass.renderPass, + m_skyPass.pipeline, skyPushConstants, { cubeDrawcall }, renderTargets); + // motion blur + vkcv::DescriptorWrites motionBlurDescriptorWrites; + motionBlurDescriptorWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, m_renderTargets.colorBuffer), + vkcv::SampledImageDescriptorWrite(1, m_renderTargets.motionBuffer) }; + motionBlurDescriptorWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(2, m_linearSampler) }; + motionBlurDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(3, m_renderTargets.motionBlurOutput) }; + + m_core.writeDescriptorSet(m_motionBlurDummyPass.descriptorSet, motionBlurDescriptorWrites); + + uint32_t fullScreenImageDispatch[3] = { + static_cast<uint32_t>((m_windowWidth + 7) / 8), + static_cast<uint32_t>((m_windowHeight + 7) / 8), + static_cast<uint32_t>(1) }; + + m_core.prepareImageForStorage(cmdStream, m_renderTargets.motionBlurOutput); + m_core.prepareImageForSampling(cmdStream, m_renderTargets.colorBuffer); + m_core.prepareImageForSampling(cmdStream, m_renderTargets.motionBuffer); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_motionBlurDummyPass.pipeline, + fullScreenImageDispatch, + { vkcv::DescriptorSetUsage(0, m_core.getDescriptorSet(m_motionBlurDummyPass.descriptorSet).vulkanHandle) }, + vkcv::PushConstants(0)); + // gamma correction - vkcv::ImageHandle gammaCorrectionInput = m_renderTargets.colorBuffer; + vkcv::ImageHandle gammaCorrectionInput = m_renderTargets.motionBlurOutput; if (drawMotionVectors) { gammaCorrectionInput = m_renderTargets.motionBuffer; } @@ -159,15 +202,10 @@ void App::run() { m_core.prepareImageForSampling(cmdStream, gammaCorrectionInput); m_core.prepareImageForStorage (cmdStream, swapchainInput); - uint32_t gammaCorrectionDispatch[3] = { - static_cast<uint32_t>((m_windowWidth + 7) / 8), - static_cast<uint32_t>((m_windowHeight + 7) / 8), - static_cast<uint32_t>(1) }; - m_core.recordComputeDispatchToCmdStream( cmdStream, m_gammaCorrectionPass.pipeline, - gammaCorrectionDispatch, + fullScreenImageDispatch, { vkcv::DescriptorSetUsage(0, m_core.getDescriptorSet(m_gammaCorrectionPass.descriptorSet).vulkanHandle) }, vkcv::PushConstants(0)); diff --git a/projects/indirect_dispatch/src/App.hpp b/projects/indirect_dispatch/src/App.hpp index 78fe382b..3dc0b908 100644 --- a/projects/indirect_dispatch/src/App.hpp +++ b/projects/indirect_dispatch/src/App.hpp @@ -21,11 +21,13 @@ private: MeshResources m_sphereMesh; MeshResources m_cubeMesh; - GraphicPassHandles m_meshPassHandles; - GraphicPassHandles m_skyPassHandles; - GraphicPassHandles m_prePassHandles; + GraphicPassHandles m_meshPass; + GraphicPassHandles m_skyPass; + GraphicPassHandles m_prePass; + GraphicPassHandles m_skyPrePass; ComputePassHandles m_gammaCorrectionPass; + ComputePassHandles m_motionBlurDummyPass; RenderTargets m_renderTargets; vkcv::SamplerHandle m_linearSampler; diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp index f827ae15..d6176c93 100644 --- a/projects/indirect_dispatch/src/AppSetup.cpp +++ b/projects/indirect_dispatch/src/AppSetup.cpp @@ -180,6 +180,28 @@ bool loadPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) { outHandles); } +bool loadSkyPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) { + assert(outHandles); + + vkcv::AttachmentDescription motionAttachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::LOAD, + AppConfig::motionBufferFormat); + + vkcv::AttachmentDescription depthAttachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::LOAD, + AppConfig::depthBufferFormat); + + return loadGraphicPass( + core, + "resources/shaders/skyPrepass.vert", + "resources/shaders/skyPrepass.frag", + vkcv::PassConfig({ motionAttachment, depthAttachment }), + vkcv::DepthTest::LessEqual, + outHandles); +} + bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, ComputePassHandles* outComputePass) { assert(outComputePass); @@ -230,6 +252,14 @@ RenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, const false, true).getHandle(); + targets.motionBlurOutput = core.createImage( + AppConfig::colorBufferFormat, + width, + height, + 1, + false, + true).getHandle(); + targets.motionBuffer = core.createImage( AppConfig::motionBufferFormat, width, diff --git a/projects/indirect_dispatch/src/AppSetup.hpp b/projects/indirect_dispatch/src/AppSetup.hpp index aa7be2fd..0253a35f 100644 --- a/projects/indirect_dispatch/src/AppSetup.hpp +++ b/projects/indirect_dispatch/src/AppSetup.hpp @@ -4,6 +4,7 @@ struct RenderTargets { vkcv::ImageHandle depthBuffer; vkcv::ImageHandle colorBuffer; + vkcv::ImageHandle motionBlurOutput; vkcv::ImageHandle motionBuffer; }; @@ -34,9 +35,10 @@ bool loadGraphicPass( const vkcv::DepthTest depthTest, GraphicPassHandles* outPassHandles); -bool loadMeshPass(vkcv::Core& core, GraphicPassHandles* outHandles); -bool loadSkyPass (vkcv::Core& core, GraphicPassHandles* outHandles); -bool loadPrePass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadMeshPass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadSkyPass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadPrePass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadSkyPrePass(vkcv::Core& core, GraphicPassHandles* outHandles); bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, ComputePassHandles* outComputePass); -- GitLab