diff --git a/projects/indirect_dispatch/resources/shaders/gammaCorrection.comp b/projects/indirect_dispatch/resources/shaders/gammaCorrection.comp index 59540806f84d4683f1ded003d05ddb98d970fe69..d1c3c049b953a87365db56d9fccf6e5fc6605def 100644 --- a/projects/indirect_dispatch/resources/shaders/gammaCorrection.comp +++ b/projects/indirect_dispatch/resources/shaders/gammaCorrection.comp @@ -9,13 +9,13 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; void main(){ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))) + ivec2 outImageRes = imageSize(outImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) return; - ivec2 textureRes = textureSize(sampler2D(inTexture, textureSampler), 0); - ivec2 coord = ivec2(gl_GlobalInvocationID.xy); - vec2 uv = vec2(coord) / textureRes; - + vec2 uv = vec2(coord) / outImageRes; vec3 linearColor = texture(sampler2D(inTexture, textureSampler), uv).rgb; // in case of motion vector visualisation negative values are possible diff --git a/projects/indirect_dispatch/resources/shaders/mesh.frag b/projects/indirect_dispatch/resources/shaders/mesh.frag index 8d032cabac816848746317097486f09ef3e4a71b..46d808c4eddb6bf87273219961c9c36db59dab74 100644 --- a/projects/indirect_dispatch/resources/shaders/mesh.frag +++ b/projects/indirect_dispatch/resources/shaders/mesh.frag @@ -2,9 +2,11 @@ #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec3 passPos; layout(location = 0) out vec3 outColor; void main() { - outColor = passNormal * 0.5 + 0.5; + // outColor = passNormal * 0.5 + 0.5; + outColor = vec3(sin(passPos.y * 100) * 0.5 + 0.5); } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/mesh.vert b/projects/indirect_dispatch/resources/shaders/mesh.vert index 5e1f72dc0d62de980502d3ccbefcaa3d425191ac..769867e3a0ebc0ce55bbf73b62d2b8a299b80b91 100644 --- a/projects/indirect_dispatch/resources/shaders/mesh.vert +++ b/projects/indirect_dispatch/resources/shaders/mesh.vert @@ -5,6 +5,7 @@ layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec3 passPos; layout( push_constant ) uniform constants{ mat4 mvp; @@ -13,4 +14,5 @@ layout( push_constant ) uniform constants{ void main() { gl_Position = mvp * vec4(inPosition, 1.0); passNormal = inNormal; + passPos = inPosition; } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp b/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp index a3a21557cd0c8ebf29fe0c1b3c1475a70d9c3731..8801203bd595983ba86b91dfc5e32ca6af8b5a90 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlurDummy.comp @@ -18,8 +18,6 @@ void main(){ 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; diff --git a/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp new file mode 100644 index 0000000000000000000000000000000000000000..f5a9fa3bfb41f6f247604f1528d19245fe6c2253 --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp @@ -0,0 +1,38 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inMotion; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outMotionMax; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +const int motionTileSize = 8; + +void main(){ + + ivec2 outImageRes = imageSize(outMotionMax); + ivec2 motionTileCoord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(motionTileCoord, outImageRes))) + return; + + float velocityMax = 0; + vec2 motionMax = vec2(0); + + ivec2 motionBufferBaseCoord = motionTileCoord * motionTileSize; + + for(int x = 0; x < motionTileSize; x++){ + for(int y = 0; y < motionTileSize; y++){ + ivec2 sampleCoord = motionBufferBaseCoord + ivec2(x, y); + vec2 motionSample = texelFetch(sampler2D(inMotion, textureSampler), sampleCoord, 0).rg; + float velocitySample = length(motionSample); + if(velocitySample > velocityMax){ + velocityMax = velocitySample; + motionMax = motionSample; + } + } + } + + imageStore(outMotionMax, motionTileCoord, vec4(motionMax, 0, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp new file mode 100644 index 0000000000000000000000000000000000000000..85d45235412d1b55d4e0868353273f60468ba497 --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp @@ -0,0 +1,36 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inMotionMax; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outMotionMaxNeighbourhood; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +const int motionTileSize = 8; + +void main(){ + + ivec2 outImageRes = imageSize(outMotionMaxNeighbourhood); + ivec2 motionTileCoord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(motionTileCoord, outImageRes))) + return; + + float velocityMax = 0; + vec2 motionMax = vec2(0); + + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + ivec2 sampleCoord = motionTileCoord + ivec2(x, y); + vec2 motionSample = texelFetch(sampler2D(inMotionMax, textureSampler), sampleCoord, 0).rg; + float velocitySample = length(motionSample); + if(velocitySample > velocityMax){ + velocityMax = velocitySample; + motionMax = motionSample; + } + } + } + + imageStore(outMotionMaxNeighbourhood, motionTileCoord, vec4(motionMax, 0, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp index 8ab6bb88255319429be0348bc1da5a6fe92732b0..fa8f260a042e86f1f62774834dd6d277183cdaf5 100644 --- a/projects/indirect_dispatch/src/App.cpp +++ b/projects/indirect_dispatch/src/App.cpp @@ -41,6 +41,12 @@ bool App::initialize() { if(!loadComputePass(m_core, "resources/shaders/motionBlurDummy.comp", &m_motionBlurDummyPass)) return false; + if (!loadComputePass(m_core, "resources/shaders/motionVectorMax.comp", &m_motionVectorMaxPass)) + return false; + + if (!loadComputePass(m_core, "resources/shaders/motionVectorMaxNeighbourhood.comp", &m_motionVectorMaxNeighbourhoodPass)) + return false; + if (!loadMesh(m_core, "resources/models/sphere.gltf", & m_sphereMesh)) return false; @@ -62,15 +68,43 @@ bool App::initialize() { void App::run() { auto frameStartTime = std::chrono::system_clock::now(); + const auto appStartTime = std::chrono::system_clock::now(); const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); const vkcv::DrawcallInfo sphereDrawcall(m_sphereMesh.mesh, {}, 1); const vkcv::DrawcallInfo cubeDrawcall(m_cubeMesh.mesh, {}, 1); vkcv::gui::GUI gui(m_core, m_window); - - bool drawMotionVectors = false; - - glm::mat4 previousFrameViewProjection = m_cameraManager.getActiveCamera().getMVP(); + enum class eDebugView : int { + None = 0, + MotionVector = 1, + MotionVectorMaxTile = 2, + MotionVectorMaxTileNeighbourhood = 3, + OptionCount = 4 }; + + const char* debugViewLabels[] = { + "None", + "Motion vectors", + "Motion vector max tiles", + "Motion vector tile neighbourhood max" }; + + enum class eMotionBlurInput : int { + MotionVector = 0, + MotionVectorMaxTile = 1, + MotionVectorMaxTileNeighbourhood = 2, + OptionCount = 3 }; + + const char* motionInputLabels[] = { + "Motion vectors", + "Motion vector max tiles", + "Motion vector tile neighbourhood max" }; + + eDebugView debugView = eDebugView::None; + eMotionBlurInput motionBlurInput = eMotionBlurInput::MotionVectorMaxTileNeighbourhood; + + float objectVerticalSpeed = 0.005; + + glm::mat4 mvpPrevious = glm::mat4(1.f); + glm::mat4 viewProjectionPrevious = m_cameraManager.getActiveCamera().getMVP(); while (m_window.isWindowOpen()) { vkcv::Window::pollEvents(); @@ -97,12 +131,18 @@ void App::run() { m_cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); const glm::mat4 viewProjection = m_cameraManager.getActiveCamera().getMVP(); + const auto time = frameEndTime - appStartTime; + const float fCurrentTime = std::chrono::duration_cast<std::chrono::milliseconds>(time).count(); + const float currentHeight = glm::sin(fCurrentTime * objectVerticalSpeed); + const glm::mat4 modelMatrix = glm::translate(glm::mat4(1), glm::vec3(0, currentHeight, 0)); + const glm::mat4 mvp = viewProjection * modelMatrix; + const vkcv::CommandStreamHandle cmdStream = m_core.createCommandStream(vkcv::QueueType::Graphics); // prepass glm::mat4 prepassMatrices[2] = { - viewProjection, - previousFrameViewProjection }; + mvp, + mvpPrevious }; vkcv::PushConstants prepassPushConstants(sizeof(glm::mat4)*2); prepassPushConstants.appendDrawcall(prepassMatrices); @@ -119,21 +159,74 @@ void App::run() { prepassRenderTargets); // sky prepass + glm::mat4 skyPrepassMatrices[2] = { + viewProjection, + viewProjectionPrevious }; + vkcv::PushConstants skyPrepassPushConstants(sizeof(glm::mat4) * 2); + skyPrepassPushConstants.appendDrawcall(skyPrepassMatrices); + m_core.recordDrawcallsToCmdStream( cmdStream, m_skyPrePass.renderPass, m_skyPrePass.pipeline, - prepassPushConstants, - { sphereDrawcall }, + skyPrepassPushConstants, + { cubeDrawcall }, prepassRenderTargets); + // motion vector max tiles + vkcv::DescriptorWrites motionVectorMaxTilesDescriptorWrites; + motionVectorMaxTilesDescriptorWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionBuffer) }; + motionVectorMaxTilesDescriptorWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, m_linearSampler) }; + motionVectorMaxTilesDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(2, m_renderTargets.motionMax)}; + + m_core.writeDescriptorSet(m_motionVectorMaxPass.descriptorSet, motionVectorMaxTilesDescriptorWrites); + + m_core.prepareImageForSampling(cmdStream, m_renderTargets.motionBuffer); + m_core.prepareImageForStorage(cmdStream, m_renderTargets.motionMax); + + const uint32_t motionTileDispatchCounts[3] = { + (m_core.getImageWidth( m_renderTargets.motionMax) + 7) / 8, + (m_core.getImageHeight(m_renderTargets.motionMax) + 7) / 8, + 1 }; + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_motionVectorMaxPass.pipeline, + motionTileDispatchCounts, + { vkcv::DescriptorSetUsage(0, m_core.getDescriptorSet(m_motionVectorMaxPass.descriptorSet).vulkanHandle) }, + vkcv::PushConstants(0)); + + // motion vector max neighbourhood + vkcv::DescriptorWrites motionVectorMaxNeighbourhoodDescriptorWrites; + motionVectorMaxNeighbourhoodDescriptorWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionMax) }; + motionVectorMaxNeighbourhoodDescriptorWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, m_linearSampler) }; + motionVectorMaxNeighbourhoodDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(2, m_renderTargets.motionMaxNeighbourhood) }; + + m_core.writeDescriptorSet(m_motionVectorMaxNeighbourhoodPass.descriptorSet, motionVectorMaxNeighbourhoodDescriptorWrites); + + m_core.prepareImageForSampling(cmdStream, m_renderTargets.motionMax); + m_core.prepareImageForStorage(cmdStream, m_renderTargets.motionMaxNeighbourhood); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_motionVectorMaxNeighbourhoodPass.pipeline, + motionTileDispatchCounts, + { vkcv::DescriptorSetUsage(0, m_core.getDescriptorSet(m_motionVectorMaxNeighbourhoodPass.descriptorSet).vulkanHandle) }, + vkcv::PushConstants(0)); + // main pass const std::vector<vkcv::ImageHandle> renderTargets = { m_renderTargets.colorBuffer, m_renderTargets.depthBuffer }; vkcv::PushConstants meshPushConstants(sizeof(glm::mat4)); - meshPushConstants.appendDrawcall(viewProjection); + meshPushConstants.appendDrawcall(mvp); m_core.recordDrawcallsToCmdStream( cmdStream, @@ -145,7 +238,7 @@ void App::run() { // sky vkcv::PushConstants skyPushConstants(sizeof(glm::mat4)); - skyPushConstants.appendDrawcall(viewProjection); + skyPushConstants.appendDrawcall(viewProjectionPrevious); m_core.recordDrawcallsToCmdStream( cmdStream, @@ -156,10 +249,22 @@ void App::run() { renderTargets); // motion blur + vkcv::ImageHandle motionBuffer; + if (motionBlurInput == eMotionBlurInput::MotionVector) + motionBuffer = m_renderTargets.motionBuffer; + else if (motionBlurInput == eMotionBlurInput::MotionVectorMaxTile) + motionBuffer = m_renderTargets.motionMax; + else if (motionBlurInput == eMotionBlurInput::MotionVectorMaxTileNeighbourhood) + motionBuffer = m_renderTargets.motionMaxNeighbourhood; + else { + vkcv_log(vkcv::LogLevel::ERROR, "Unknown eMotionInput enum value"); + motionBuffer = m_renderTargets.motionBuffer; + } + vkcv::DescriptorWrites motionBlurDescriptorWrites; motionBlurDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, m_renderTargets.colorBuffer), - vkcv::SampledImageDescriptorWrite(1, m_renderTargets.motionBuffer) }; + vkcv::SampledImageDescriptorWrite(1, motionBuffer) }; motionBlurDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, m_linearSampler) }; motionBlurDescriptorWrites.storageImageWrites = { @@ -174,7 +279,7 @@ void App::run() { m_core.prepareImageForStorage(cmdStream, m_renderTargets.motionBlurOutput); m_core.prepareImageForSampling(cmdStream, m_renderTargets.colorBuffer); - m_core.prepareImageForSampling(cmdStream, m_renderTargets.motionBuffer); + m_core.prepareImageForSampling(cmdStream, motionBuffer); m_core.recordComputeDispatchToCmdStream( cmdStream, @@ -184,9 +289,18 @@ void App::run() { vkcv::PushConstants(0)); // gamma correction - vkcv::ImageHandle gammaCorrectionInput = m_renderTargets.motionBlurOutput; - if (drawMotionVectors) { + vkcv::ImageHandle gammaCorrectionInput; + if (debugView == eDebugView::None) + gammaCorrectionInput = m_renderTargets.motionBlurOutput; + else if (debugView == eDebugView::MotionVector) gammaCorrectionInput = m_renderTargets.motionBuffer; + else if (debugView == eDebugView::MotionVectorMaxTile) + gammaCorrectionInput = m_renderTargets.motionMax; + else if (debugView == eDebugView::MotionVectorMaxTileNeighbourhood) + gammaCorrectionInput = m_renderTargets.motionMaxNeighbourhood; + else { + vkcv_log(vkcv::LogLevel::ERROR, "Unknown eDebugView enum value"); + gammaCorrectionInput = m_renderTargets.motionBlurOutput; } vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; @@ -214,12 +328,27 @@ void App::run() { gui.beginGUI(); ImGui::Begin("Settings"); - ImGui::Checkbox("View motion vectors", &drawMotionVectors); + + ImGui::Combo( + "Debug view", + reinterpret_cast<int*>(&debugView), + debugViewLabels, + static_cast<int>(eDebugView::OptionCount)); + + ImGui::Combo( + "Motion blur input", + reinterpret_cast<int*>(&motionBlurInput), + motionInputLabels, + static_cast<int>(eMotionBlurInput::OptionCount)); + + ImGui::InputFloat("Object movement speed", &objectVerticalSpeed); + ImGui::End(); gui.endGUI(); m_core.endFrame(); - previousFrameViewProjection = viewProjection; + viewProjectionPrevious = viewProjection; + mvpPrevious = mvp; } } \ No newline at end of file diff --git a/projects/indirect_dispatch/src/App.hpp b/projects/indirect_dispatch/src/App.hpp index 3dc0b908271297dfca22b5ff551c71d0a3ad6fe7..9bbdde94f05ac7859b45c03e57ac91f1409a1dd3 100644 --- a/projects/indirect_dispatch/src/App.hpp +++ b/projects/indirect_dispatch/src/App.hpp @@ -28,6 +28,8 @@ private: ComputePassHandles m_gammaCorrectionPass; ComputePassHandles m_motionBlurDummyPass; + ComputePassHandles m_motionVectorMaxPass; + ComputePassHandles m_motionVectorMaxNeighbourhoodPass; RenderTargets m_renderTargets; vkcv::SamplerHandle m_linearSampler; diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp index d6176c937e2d333c899395421fc6c25ef1226211..96b182fd330feef3a128cf3a5bce4e8678372a59 100644 --- a/projects/indirect_dispatch/src/AppSetup.cpp +++ b/projects/indirect_dispatch/src/AppSetup.cpp @@ -269,5 +269,26 @@ RenderTargets createRenderTargets(vkcv::Core& core, const uint32_t width, const false, true).getHandle(); + const uint32_t motionTileSize = 8; + // divide and ceil to int + const uint32_t motionMaxWidth = (width + (motionTileSize - 1)) / motionTileSize; + const uint32_t motionMaxheight = (height + (motionTileSize - 1)) / motionTileSize; + + targets.motionMax = core.createImage( + AppConfig::motionBufferFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true).getHandle(); + + targets.motionMaxNeighbourhood = core.createImage( + AppConfig::motionBufferFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true).getHandle(); + return targets; } \ No newline at end of file diff --git a/projects/indirect_dispatch/src/AppSetup.hpp b/projects/indirect_dispatch/src/AppSetup.hpp index 0253a35fe73ad9a1c9fb6a4c2b24262214c6ac6d..adb8c1985c86665f40a67c41bad639d6ef0a64dc 100644 --- a/projects/indirect_dispatch/src/AppSetup.hpp +++ b/projects/indirect_dispatch/src/AppSetup.hpp @@ -6,6 +6,8 @@ struct RenderTargets { vkcv::ImageHandle colorBuffer; vkcv::ImageHandle motionBlurOutput; vkcv::ImageHandle motionBuffer; + vkcv::ImageHandle motionMax; + vkcv::ImageHandle motionMaxNeighbourhood; }; struct GraphicPassHandles {