From 3c2a55431c1472cbab056eb122f1c41f7d89b33d Mon Sep 17 00:00:00 2001 From: Alexander Gauggel <agauggel@uni-koblenz.de> Date: Sun, 22 Aug 2021 13:12:07 +0200 Subject: [PATCH] [#106] Add fast path for motion blur --- .../resources/shaders/motionBlur.comp | 36 +---- .../resources/shaders/motionBlur.inc | 35 ++++ .../shaders/motionBlurColorCopy.comp | 2 +- .../resources/shaders/motionBlurFastPath.comp | 68 ++++++++ .../shaders/motionBlurTileClassification.comp | 45 ++++-- .../motionBlurTileClassificationVis.comp | 13 +- .../shaders/motionBlurWorkTileReset.comp | 12 +- .../shaders/motionVectorMaxNeighbourhood.comp | 34 ---- ...VectorMax.comp => motionVectorMinMax.comp} | 13 +- .../motionVectorMinMaxNeighbourhood.comp | 51 ++++++ projects/indirect_dispatch/src/App.cpp | 44 +----- projects/indirect_dispatch/src/MotionBlur.cpp | 149 +++++++++++------- projects/indirect_dispatch/src/MotionBlur.hpp | 38 +++-- .../indirect_dispatch/src/MotionBlurSetup.cpp | 16 ++ .../indirect_dispatch/src/MotionBlurSetup.hpp | 2 + 15 files changed, 366 insertions(+), 192 deletions(-) create mode 100644 projects/indirect_dispatch/resources/shaders/motionBlur.inc create mode 100644 projects/indirect_dispatch/resources/shaders/motionBlurFastPath.comp delete mode 100644 projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp rename projects/indirect_dispatch/resources/shaders/{motionVectorMax.comp => motionVectorMinMax.comp} (73%) create mode 100644 projects/indirect_dispatch/resources/shaders/motionVectorMinMaxNeighbourhood.comp diff --git a/projects/indirect_dispatch/resources/shaders/motionBlur.comp b/projects/indirect_dispatch/resources/shaders/motionBlur.comp index 7d71df17..9fa4f97c 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlur.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlur.comp @@ -1,6 +1,7 @@ #version 440 #extension GL_GOOGLE_include_directive : enable +#include "motionBlur.inc" #include "motionBlurConfig.inc" #include "motionBlurWorkTile.inc" @@ -11,7 +12,7 @@ layout(set=0, binding=3) uniform texture2D inMotionNeighbou layout(set=0, binding=4) uniform sampler nearestSampler; layout(set=0, binding=5, r11f_g11f_b10f) uniform image2D outImage; -layout(set=0, binding=6) buffer copyPathTileBuffer { +layout(set=0, binding=6) buffer WorkTileBuffer { WorkTiles workTiles; }; @@ -89,29 +90,12 @@ float computeSampleWeigth(SampleData mainPixel, SampleData samplePixel){ depthClassification.background * pointSpread.background; } -// see "A Reconstruction Filter for Plausible Motion Blur", section 2.2 -vec2 processMotionVector(vec2 motion){ - // every frame a pixel should blur over the distance it moves - // as we blur in two directions (where it was and where it will be) we must half the motion - vec2 motionHalf = motion * 0.5; - vec2 motionScaled = motionHalf * motionScaleFactor; // scale factor contains shutter speed and delta time - - float velocityPixels = length(motionScaled * imageSize(outImage)); - float epsilon = 0.0001; - - // pixels are anisotropic, so the ratio for clamping the velocity is computed in pixels instead of uv coordinates - vec2 motionPixel = motionScaled * imageSize(outImage); - - // this clamps the motion to not exceed the radius given by the motion tile size - return motionScaled * max(0.5, min(velocityPixels, motionTileSize)) / (velocityPixels + epsilon); -} - SampleData loadSampleData(vec2 uv){ SampleData data; data.color = texture(sampler2D(inColor, nearestSampler), uv).rgb; data.coordinate = ivec2(uv * imageSize(outImage)); - data.motion = processMotionVector(texture(sampler2D(inMotionFullRes, nearestSampler), uv).rg); + data.motion = processMotionVector(texture(sampler2D(inMotionFullRes, nearestSampler), uv).rg, motionScaleFactor, imageSize(outImage)); data.velocityPixels = length(data.motion * imageSize(outImage)); data.depthLinear = texture(sampler2D(inDepth, nearestSampler), uv).r; data.depthLinear = linearizeDepth(data.depthLinear, cameraNearPlane, cameraFarPlane); @@ -119,18 +103,6 @@ SampleData loadSampleData(vec2 uv){ return data; } -const int ditherSize = 4; - -// simple binary dither pattern -// could be optimized to avoid modulo and branch -float dither(ivec2 coord){ - - bool x = coord.x % ditherSize < (ditherSize / 2); - bool y = coord.y % ditherSize < (ditherSize / 2); - - return x ^^ y ? 1 : 0; -} - void main(){ uint tileIndex = gl_WorkGroupID.x; @@ -146,7 +118,7 @@ void main(){ // the motion tile lookup is jittered, so the hard edges in the blur are replaced by noise // dither is shifted, so it does not line up with motion tiles vec2 motionOffset = motionTileOffsetLength * (dither(coord + ivec2(ditherSize / 2)) * 2 - 1) / textureRes; - vec2 motionNeighbourhoodMax = processMotionVector(texture(sampler2D(inMotionNeighbourhoodMax, nearestSampler), uv + motionOffset).rg); + vec2 motionNeighbourhoodMax = processMotionVector(texture(sampler2D(inMotionNeighbourhoodMax, nearestSampler), uv + motionOffset).rg, motionScaleFactor, imageSize(outImage)); SampleData mainPixel = loadSampleData(uv); diff --git a/projects/indirect_dispatch/resources/shaders/motionBlur.inc b/projects/indirect_dispatch/resources/shaders/motionBlur.inc new file mode 100644 index 00000000..6fdaf4c5 --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionBlur.inc @@ -0,0 +1,35 @@ +#ifndef MOTION_BLUR +#define MOTION_BLUR + +#include "motionBlurConfig.inc" + +// see "A Reconstruction Filter for Plausible Motion Blur", section 2.2 +vec2 processMotionVector(vec2 motion, float motionScaleFactor, ivec2 imageResolution){ + // every frame a pixel should blur over the distance it moves + // as we blur in two directions (where it was and where it will be) we must half the motion + vec2 motionHalf = motion * 0.5; + vec2 motionScaled = motionHalf * motionScaleFactor; // scale factor contains shutter speed and delta time + + // pixels are anisotropic, so the ratio for clamping the velocity is computed in pixels instead of uv coordinates + vec2 motionPixel = motionScaled * imageResolution; + float velocityPixels = length(motionPixel); + + float epsilon = 0.0001; + + // this clamps the motion to not exceed the radius given by the motion tile size + return motionScaled * max(0.5, min(velocityPixels, motionTileSize)) / (velocityPixels + epsilon); +} + +const int ditherSize = 4; + +// simple binary dither pattern +// could be optimized to avoid modulo and branch +float dither(ivec2 coord){ + + bool x = coord.x % ditherSize < (ditherSize / 2); + bool y = coord.y % ditherSize < (ditherSize / 2); + + return x ^^ y ? 1 : 0; +} + +#endif // #ifndef MOTION_BLUR \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurColorCopy.comp b/projects/indirect_dispatch/resources/shaders/motionBlurColorCopy.comp index 3afbe9f3..1d8f210c 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlurColorCopy.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlurColorCopy.comp @@ -8,7 +8,7 @@ layout(set=0, binding=0) uniform texture2D inColor; layout(set=0, binding=1) uniform sampler nearestSampler; layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; -layout(set=0, binding=3) buffer copyPathTileBuffer { +layout(set=0, binding=3) buffer WorkTileBuffer { WorkTiles workTiles; }; diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurFastPath.comp b/projects/indirect_dispatch/resources/shaders/motionBlurFastPath.comp new file mode 100644 index 00000000..e2967bac --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionBlurFastPath.comp @@ -0,0 +1,68 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlur.inc" +#include "motionBlurConfig.inc" +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform texture2D inMotionNeighbourhoodMax; +layout(set=0, binding=2) uniform sampler nearestSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D outImage; + +layout(set=0, binding=4) buffer WorkTileBuffer { + WorkTiles workTiles; +}; + +layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + // computed from delta time and shutter speed + float motionScaleFactor; +}; + +void main(){ + + uint tileIndex = gl_WorkGroupID.x; + ivec2 tileCoordinates = workTiles.tileXY[tileIndex]; + ivec2 coord = ivec2(tileCoordinates * motionTileSize + gl_LocalInvocationID.xy); + + if(any(greaterThanEqual(coord, imageSize(outImage)))) + return; + + ivec2 textureRes = textureSize(sampler2D(inColor, nearestSampler), 0); + vec2 uv = vec2(coord + 0.5) / textureRes; // + 0.5 to shift uv into pixel center + + vec2 motionNeighbourhoodMax = processMotionVector(texture(sampler2D(inMotionNeighbourhoodMax, nearestSampler), uv).rg, motionScaleFactor, imageSize(outImage)); + + // early out on movement less than half a pixel + if(length(motionNeighbourhoodMax * imageSize(outImage)) <= 0.5){ + vec3 color = texture(sampler2D(inColor, nearestSampler), uv).rgb; + imageStore(outImage, coord, vec4(color, 0.f)); + return; + } + + vec3 color = vec3(0); + + // clamping start and end points avoids artifacts at image borders + // the sampler clamps the sample uvs anyways, but without clamping here, many samples can be stuck at the border + vec2 uvStart = clamp(uv - motionNeighbourhoodMax, 0, 1); + vec2 uvEnd = clamp(uv + motionNeighbourhoodMax, 0, 1); + + // samples are placed evenly, but the entire filter is jittered + // dither returns either 0 or 1 + // the sampleUV code expects an offset in range [-0.5, 0.5], so the dither is rescaled to a binary -0.25/0.25 + float random = dither(coord) * 0.5 - 0.25; + + const int sampleCount = 16; + + for(int i = 0; i < sampleCount; i++){ + + vec2 sampleUV = mix(uvStart, uvEnd, (i + random + 1) / float(sampleCount + 1)); + color += texture(sampler2D(inColor, nearestSampler), 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/motionBlurTileClassification.comp b/projects/indirect_dispatch/resources/shaders/motionBlurTileClassification.comp index a7be26b8..3c6f9e37 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlurTileClassification.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlurTileClassification.comp @@ -3,41 +3,56 @@ #include "motionBlurWorkTile.inc" -layout(set=0, binding=0) uniform texture2D inVelocityTile; -layout(set=0, binding=1) uniform sampler nearestSampler; +layout(set=0, binding=0) uniform texture2D inMotionMax; +layout(set=0, binding=1) uniform texture2D inMotionMin; +layout(set=0, binding=2) uniform sampler nearestSampler; -layout(set=0, binding=2) buffer fullPathTileBuffer { +layout(set=0, binding=3) buffer FullPathTileBuffer { WorkTiles fullPathTiles; }; -layout(set=0, binding=3) buffer copyPathTileBuffer { +layout(set=0, binding=4) buffer CopyPathTileBuffer { WorkTiles copyPathTiles; }; +layout(set=0, binding=5) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout( push_constant ) uniform constants{ - uint width; - uint height; + uint width; + uint height; + float fastPathThreshold; }; void main(){ ivec2 tileCoord = ivec2(gl_GlobalInvocationID.xy); - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, textureSize(sampler2D(inVelocityTile, nearestSampler), 0)))) + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, textureSize(sampler2D(inMotionMax, nearestSampler), 0)))) return; - vec2 motion = texelFetch(sampler2D(inVelocityTile, nearestSampler), tileCoord, 0).rg; - vec2 motionPixel = motion * vec2(width, height); - float velocityPixel = length(motionPixel); + vec2 motionMax = texelFetch(sampler2D(inMotionMax, nearestSampler), tileCoord, 0).rg; + vec2 motionMin = texelFetch(sampler2D(inMotionMin, nearestSampler), tileCoord, 0).rg; - if(velocityPixel > 0.5){ - uint index = atomicAdd(fullPathTiles.tileCount, 1); - fullPathTiles.tileXY[index] = tileCoord; - } - else{ + vec2 motionPixelMax = motionMax * vec2(width, height); + vec2 motionPixelMin = motionMin * vec2(width, height); + + float velocityPixelMax = length(motionPixelMax); + float minMaxDistance = distance(motionPixelMin, motionPixelMax); + + if(velocityPixelMax <= 0.5){ uint index = atomicAdd(copyPathTiles.tileCount, 1); copyPathTiles.tileXY[index] = tileCoord; } + else if(minMaxDistance <= fastPathThreshold){ + uint index = atomicAdd(fastPathTiles.tileCount, 1); + fastPathTiles.tileXY[index] = tileCoord; + } + else{ + uint index = atomicAdd(fullPathTiles.tileCount, 1); + fullPathTiles.tileXY[index] = tileCoord; + } } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurTileClassificationVis.comp b/projects/indirect_dispatch/resources/shaders/motionBlurTileClassificationVis.comp index 7889f454..3382ff5e 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlurTileClassificationVis.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlurTileClassificationVis.comp @@ -8,20 +8,25 @@ layout(set=0, binding=0) uniform texture2D inColor; layout(set=0, binding=1) uniform sampler nearestSampler; layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; -layout(set=0, binding=3) buffer fullPathTileBuffer { +layout(set=0, binding=3) buffer FullPathTileBuffer { WorkTiles fullPathTiles; }; -layout(set=0, binding=4) buffer copyPathTileBuffer { +layout(set=0, binding=4) buffer CopyPathTileBuffer { WorkTiles copyPathTiles; }; +layout(set=0, binding=5) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; void main(){ uint tileIndexFullPath = gl_WorkGroupID.x; uint tileIndexCopyPath = gl_WorkGroupID.x - fullPathTiles.tileCount; + uint tileIndexFastPath = gl_WorkGroupID.x - fullPathTiles.tileCount - copyPathTiles.tileCount; vec3 debugColor; ivec2 tileCoordinates; @@ -34,6 +39,10 @@ void main(){ debugColor = vec3(0, 1, 0); tileCoordinates = copyPathTiles.tileXY[tileIndexCopyPath]; } + else if(tileIndexFastPath < fastPathTiles.tileCount){ + debugColor = vec3(0, 0, 1); + tileCoordinates = fastPathTiles.tileXY[tileIndexFastPath]; + } else{ return; } diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurWorkTileReset.comp b/projects/indirect_dispatch/resources/shaders/motionBlurWorkTileReset.comp index 916a4e4d..d4b55582 100644 --- a/projects/indirect_dispatch/resources/shaders/motionBlurWorkTileReset.comp +++ b/projects/indirect_dispatch/resources/shaders/motionBlurWorkTileReset.comp @@ -3,14 +3,18 @@ #include "motionBlurWorkTile.inc" -layout(set=0, binding=0) buffer fullPathTileBuffer { +layout(set=0, binding=0) buffer FullPathTileBuffer { WorkTiles fullPathTiles; }; -layout(set=0, binding=1) buffer copyPathTileBuffer { +layout(set=0, binding=1) buffer CopyPathTileBuffer { WorkTiles copyPathTiles; }; +layout(set=0, binding=2) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; void main(){ @@ -21,4 +25,8 @@ void main(){ copyPathTiles.tileCount = 0; copyPathTiles.dispatchY = 1; copyPathTiles.dispatchZ = 1; + + fastPathTiles.tileCount = 0; + fastPathTiles.dispatchY = 1; + fastPathTiles.dispatchZ = 1; } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp deleted file mode 100644 index 90f9acce..00000000 --- a/projects/indirect_dispatch/resources/shaders/motionVectorMaxNeighbourhood.comp +++ /dev/null @@ -1,34 +0,0 @@ -#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; - -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/resources/shaders/motionVectorMax.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMinMax.comp similarity index 73% rename from projects/indirect_dispatch/resources/shaders/motionVectorMax.comp rename to projects/indirect_dispatch/resources/shaders/motionVectorMinMax.comp index 65a6186c..4ad350b0 100644 --- a/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp +++ b/projects/indirect_dispatch/resources/shaders/motionVectorMinMax.comp @@ -4,7 +4,8 @@ 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(set=0, binding=2, rg16) uniform image2D outMotionMax; +layout(set=0, binding=3, rg16) uniform image2D outMotionMin; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; @@ -19,6 +20,9 @@ void main(){ float velocityMax = 0; vec2 motionMax = vec2(0); + float velocityMin = 100000; + vec2 motionMin = vec2(0); + ivec2 motionBufferBaseCoord = motionTileCoord * motionTileSize; for(int x = 0; x < motionTileSize; x++){ @@ -26,12 +30,19 @@ void main(){ 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; } + + if(velocitySample < velocityMin){ + velocityMin = velocitySample; + motionMin = motionSample; + } } } imageStore(outMotionMax, motionTileCoord, vec4(motionMax, 0, 0)); + imageStore(outMotionMin, motionTileCoord, vec4(motionMin, 0, 0)); } \ No newline at end of file diff --git a/projects/indirect_dispatch/resources/shaders/motionVectorMinMaxNeighbourhood.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMinMaxNeighbourhood.comp new file mode 100644 index 00000000..4d6e7c0a --- /dev/null +++ b/projects/indirect_dispatch/resources/shaders/motionVectorMinMaxNeighbourhood.comp @@ -0,0 +1,51 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inMotionMax; +layout(set=0, binding=1) uniform texture2D inMotionMin; +layout(set=0, binding=2) uniform sampler textureSampler; +layout(set=0, binding=3, rg16) uniform image2D outMotionMaxNeighbourhood; +layout(set=0, binding=4, rg16) uniform image2D outMotionMinNeighbourhood; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +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); + + float velocityMin = 10000; + vec2 motionMin = vec2(0); + + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + ivec2 sampleCoord = motionTileCoord + ivec2(x, y); + + vec2 motionSampleMax = texelFetch(sampler2D(inMotionMax, textureSampler), sampleCoord, 0).rg; + float velocitySampleMax = length(motionSampleMax); + + if(velocitySampleMax > velocityMax){ + velocityMax = velocitySampleMax; + motionMax = motionSampleMax; + } + + + vec2 motionSampleMin = texelFetch(sampler2D(inMotionMin, textureSampler), sampleCoord, 0).rg; + float velocitySampleMin = length(motionSampleMin); + + if(velocitySampleMin < velocityMin){ + velocityMin = velocitySampleMin; + motionMin = motionSampleMin; + } + } + } + + imageStore(outMotionMaxNeighbourhood, motionTileCoord, vec4(motionMax, 0, 0)); + imageStore(outMotionMinNeighbourhood, motionTileCoord, vec4(motionMin, 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 2b46be6b..92d548ac 100644 --- a/projects/indirect_dispatch/src/App.cpp +++ b/projects/indirect_dispatch/src/App.cpp @@ -80,22 +80,8 @@ void App::run() { vkcv::gui::GUI gui(m_core, m_window); - enum class eMotionVectorVisualisationMode : int { - None = 0, - FullResolution = 1, - MaxTile = 2, - MaxTileNeighbourhood = 3, - OptionCount = 4 }; - - const char* motionVectorVisualisationModeLabels[] = { - "None", - "Full resolution", - "Max tiles", - "Tile neighbourhood max" }; - eMotionVectorVisualisationMode motionVectorVisualisationMode = eMotionVectorVisualisationMode::None; - eMotionVectorMode motionBlurMotionMode = eMotionVectorMode::MaxTileNeighbourhood; - eMotionBlurMode motionBlurMode = eMotionBlurMode::Default; + eMotionBlurMode motionBlurMode = eMotionBlurMode::Default; bool freezeFrame = false; float motionBlurTileOffsetLength = 3; @@ -106,6 +92,7 @@ void App::run() { float objectRotationSpeedY = 5; int cameraShutterSpeedInverse = 24; float motionVectorVisualisationRange = 0.008; + float motionBlurFastPathThreshold = 1; glm::mat4 viewProjection = m_cameraManager.getActiveCamera().getMVP(); glm::mat4 viewProjectionPrevious = m_cameraManager.getActiveCamera().getMVP(); @@ -299,31 +286,19 @@ void App::run() { m_renderTargets.motionBuffer, m_renderTargets.colorBuffer, m_renderTargets.depthBuffer, - motionBlurMotionMode, motionBlurMode, cameraNear, cameraFar, fDeltaTimeSeconds, cameraShutterSpeedInverse, - motionBlurTileOffsetLength); + motionBlurTileOffsetLength, + motionBlurFastPathThreshold); } else { - eMotionVectorMode debugViewMode; - if (motionVectorVisualisationMode == eMotionVectorVisualisationMode::FullResolution) - debugViewMode = eMotionVectorMode::FullResolution; - else if(motionVectorVisualisationMode == eMotionVectorVisualisationMode::MaxTile) - debugViewMode = eMotionVectorMode::MaxTile; - else if (motionVectorVisualisationMode == eMotionVectorVisualisationMode::MaxTileNeighbourhood) - debugViewMode = eMotionVectorMode::MaxTileNeighbourhood; - else { - vkcv_log(vkcv::LogLevel::ERROR, "Unknown eMotionVectorMode enum option"); - debugViewMode = eMotionVectorMode::FullResolution; - } - motionBlurOutput = m_motionBlur.renderMotionVectorVisualisation( cmdStream, m_renderTargets.motionBuffer, - debugViewMode, + motionVectorVisualisationMode, motionVectorVisualisationRange); } @@ -361,6 +336,7 @@ void App::run() { ImGui::Checkbox("Freeze frame", &freezeFrame); ImGui::InputFloat("Motion tile offset length", &motionBlurTileOffsetLength); + ImGui::InputFloat("Motion blur fast path threshold", &motionBlurFastPathThreshold); ImGui::Combo( "Motion blur mode", @@ -371,18 +347,12 @@ void App::run() { ImGui::Combo( "Debug view", reinterpret_cast<int*>(&motionVectorVisualisationMode), - motionVectorVisualisationModeLabels, + MotionVectorVisualisationModeLabels, static_cast<int>(eMotionVectorVisualisationMode::OptionCount)); if (motionVectorVisualisationMode != eMotionVectorVisualisationMode::None) ImGui::InputFloat("Motion vector visualisation range", &motionVectorVisualisationRange); - ImGui::Combo( - "Motion blur input", - reinterpret_cast<int*>(&motionBlurMotionMode), - MotionVectorModeLabels, - static_cast<int>(eMotionVectorMode::OptionCount)); - ImGui::InputInt("Camera shutter speed inverse", &cameraShutterSpeedInverse); ImGui::InputFloat("Object movement speed", &objectVerticalSpeed); diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp index 223b74ca..b3cf6df8 100644 --- a/projects/indirect_dispatch/src/MotionBlur.cpp +++ b/projects/indirect_dispatch/src/MotionBlur.cpp @@ -27,10 +27,10 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con if (!loadComputePass(*m_core, "resources/shaders/motionBlur.comp", &m_motionBlurPass)) return false; - if (!loadComputePass(*m_core, "resources/shaders/motionVectorMax.comp", &m_motionVectorMaxPass)) + if (!loadComputePass(*m_core, "resources/shaders/motionVectorMinMax.comp", &m_motionVectorMinMaxPass)) return false; - if (!loadComputePass(*m_core, "resources/shaders/motionVectorMaxNeighbourhood.comp", &m_motionVectorMaxNeighbourhoodPass)) + if (!loadComputePass(*m_core, "resources/shaders/motionVectorMinMaxNeighbourhood.comp", &m_motionVectorMinMaxNeighbourhoodPass)) return false; if (!loadComputePass(*m_core, "resources/shaders/motionVectorVisualisation.comp", &m_motionVectorVisualisationPass)) @@ -48,6 +48,9 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con if (!loadComputePass(*m_core, "resources/shaders/motionBlurTileClassificationVis.comp", &m_tileVisualisationPass)) return false; + if (!loadComputePass(*m_core, "resources/shaders/motionBlurFastPath.comp", &m_motionBlurFastPathPass)) + return false; + // work tile buffers and descriptors const uint32_t workTileBufferSize = static_cast<uint32_t>(2 * sizeof(uint32_t)) * (3 + ((MotionBlurConfig::maxWidth + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize) * @@ -65,10 +68,17 @@ bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, con vkcv::BufferMemoryType::DEVICE_LOCAL, true).getHandle(); + m_fastPathWorkTileBuffer = m_core->createBuffer<uint32_t>( + vkcv::BufferType::STORAGE, + workTileBufferSize, + vkcv::BufferMemoryType::DEVICE_LOCAL, + true).getHandle(); + vkcv::DescriptorWrites tileResetDescriptorWrites; tileResetDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_fullPathWorkTileBuffer), - vkcv::BufferDescriptorWrite(1, m_copyPathWorkTileBuffer) }; + vkcv::BufferDescriptorWrite(1, m_copyPathWorkTileBuffer), + vkcv::BufferDescriptorWrite(2, m_fastPathWorkTileBuffer) }; m_core->writeDescriptorSet(m_tileResetPass.descriptorSet, tileResetDescriptorWrites); @@ -93,13 +103,13 @@ vkcv::ImageHandle MotionBlur::render( const vkcv::ImageHandle motionBufferFullRes, const vkcv::ImageHandle colorBuffer, const vkcv::ImageHandle depthBuffer, - const eMotionVectorMode motionVectorMode, const eMotionBlurMode mode, const float cameraNear, const float cameraFar, const float deltaTimeSeconds, const float cameraShutterSpeedInverse, - const float motionTileOffsetLength) { + const float motionTileOffsetLength, + const float fastPathThreshold) { computeMotionTiles(cmdStream, motionBufferFullRes); @@ -115,16 +125,19 @@ vkcv::ImageHandle MotionBlur::render( m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer); m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer); + m_core->recordBufferMemoryBarrier(cmdStream, m_fastPathWorkTileBuffer); // work tile classification vkcv::DescriptorWrites tileClassificationDescriptorWrites; tileClassificationDescriptorWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionMaxNeighbourhood) }; + vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionMaxNeighbourhood), + vkcv::SampledImageDescriptorWrite(1, m_renderTargets.motionMinNeighbourhood) }; tileClassificationDescriptorWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, m_nearestSampler) }; + vkcv::SamplerDescriptorWrite(2, m_nearestSampler) }; tileClassificationDescriptorWrites.storageBufferWrites = { - vkcv::BufferDescriptorWrite(2, m_fullPathWorkTileBuffer), - vkcv::BufferDescriptorWrite(3, m_copyPathWorkTileBuffer) }; + vkcv::BufferDescriptorWrite(3, m_fullPathWorkTileBuffer), + vkcv::BufferDescriptorWrite(4, m_copyPathWorkTileBuffer), + vkcv::BufferDescriptorWrite(5, m_fastPathWorkTileBuffer) }; m_core->writeDescriptorSet(m_tileClassificationPass.descriptorSet, tileClassificationDescriptorWrites); @@ -133,47 +146,39 @@ vkcv::ImageHandle MotionBlur::render( m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood), 8); - struct ResolutionConstants { - uint32_t width; - uint32_t height; + struct ClassificationConstants { + uint32_t width; + uint32_t height; + float fastPathThreshold; }; - vkcv::PushConstants resolutionPushConstants(sizeof(ResolutionConstants)); - ResolutionConstants resolutionConstants; - resolutionConstants.width = m_core->getImageWidth(m_renderTargets.outputColor); - resolutionConstants.height = m_core->getImageHeight(m_renderTargets.outputColor); - resolutionPushConstants.appendDrawcall(resolutionConstants); + ClassificationConstants classificationConstants; + classificationConstants.width = m_core->getImageWidth(m_renderTargets.outputColor); + classificationConstants.height = m_core->getImageHeight(m_renderTargets.outputColor); + classificationConstants.fastPathThreshold = fastPathThreshold; + + vkcv::PushConstants classificationPushConstants(sizeof(ClassificationConstants)); + classificationPushConstants.appendDrawcall(classificationConstants); m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMaxNeighbourhood); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMinNeighbourhood); m_core->recordComputeDispatchToCmdStream( cmdStream, m_tileClassificationPass.pipeline, tileClassificationDispatch.data(), { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_tileClassificationPass.descriptorSet).vulkanHandle) }, - resolutionPushConstants); + classificationPushConstants); m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer); m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer); - - // usually this is the neighbourhood max, but other modes can be used for comparison/debugging - vkcv::ImageHandle inputMotionTiles; - if (motionVectorMode == eMotionVectorMode::FullResolution) - inputMotionTiles = motionBufferFullRes; - else if (motionVectorMode == eMotionVectorMode::MaxTile) - inputMotionTiles = m_renderTargets.motionMax; - else if (motionVectorMode == eMotionVectorMode::MaxTileNeighbourhood) - inputMotionTiles = m_renderTargets.motionMaxNeighbourhood; - else { - vkcv_log(vkcv::LogLevel::ERROR, "Unknown eMotionInput enum value"); - inputMotionTiles = m_renderTargets.motionMaxNeighbourhood; - } + m_core->recordBufferMemoryBarrier(cmdStream, m_fastPathWorkTileBuffer); vkcv::DescriptorWrites motionBlurDescriptorWrites; motionBlurDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, colorBuffer), vkcv::SampledImageDescriptorWrite(1, depthBuffer), vkcv::SampledImageDescriptorWrite(2, motionBufferFullRes), - vkcv::SampledImageDescriptorWrite(3, inputMotionTiles) }; + vkcv::SampledImageDescriptorWrite(3, m_renderTargets.motionMaxNeighbourhood) }; motionBlurDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(4, m_nearestSampler) }; motionBlurDescriptorWrites.storageImageWrites = { @@ -196,6 +201,20 @@ vkcv::ImageHandle MotionBlur::render( m_core->writeDescriptorSet(m_colorCopyPass.descriptorSet, colorCopyDescriptorWrites); + + vkcv::DescriptorWrites fastPathDescriptorWrites; + fastPathDescriptorWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, colorBuffer), + vkcv::SampledImageDescriptorWrite(1, m_renderTargets.motionMaxNeighbourhood) }; + fastPathDescriptorWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(2, m_nearestSampler) }; + fastPathDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(3, m_renderTargets.outputColor) }; + fastPathDescriptorWrites.storageBufferWrites = { + vkcv::BufferDescriptorWrite(4, m_fastPathWorkTileBuffer) }; + + m_core->writeDescriptorSet(m_motionBlurFastPathPass.descriptorSet, fastPathDescriptorWrites); + // must match layout in "motionBlur.comp" struct MotionBlurConstantData { float motionFactor; @@ -218,7 +237,7 @@ vkcv::ImageHandle MotionBlur::render( m_core->prepareImageForStorage(cmdStream, m_renderTargets.outputColor); m_core->prepareImageForSampling(cmdStream, colorBuffer); m_core->prepareImageForSampling(cmdStream, depthBuffer); - m_core->prepareImageForSampling(cmdStream, inputMotionTiles); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMaxNeighbourhood); if (mode == eMotionBlurMode::Default) { m_core->recordComputeIndirectDispatchToCmdStream( @@ -236,6 +255,14 @@ vkcv::ImageHandle MotionBlur::render( 0, { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_colorCopyPass.descriptorSet).vulkanHandle) }, vkcv::PushConstants(0)); + + m_core->recordComputeIndirectDispatchToCmdStream( + cmdStream, + m_motionBlurFastPathPass.pipeline, + m_fastPathWorkTileBuffer, + 0, + { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionBlurFastPathPass.descriptorSet).vulkanHandle) }, + vkcv::PushConstants(0)); } else if(mode == eMotionBlurMode::Disabled) { return colorBuffer; @@ -251,7 +278,8 @@ vkcv::ImageHandle MotionBlur::render( vkcv::StorageImageDescriptorWrite(2, m_renderTargets.outputColor)}; visualisationDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(3, m_fullPathWorkTileBuffer), - vkcv::BufferDescriptorWrite(4, m_copyPathWorkTileBuffer)}; + vkcv::BufferDescriptorWrite(4, m_copyPathWorkTileBuffer), + vkcv::BufferDescriptorWrite(5, m_fastPathWorkTileBuffer) }; m_core->writeDescriptorSet(m_tileVisualisationPass.descriptorSet, visualisationDescriptorWrites); @@ -280,20 +308,28 @@ vkcv::ImageHandle MotionBlur::render( } vkcv::ImageHandle MotionBlur::renderMotionVectorVisualisation( - const vkcv::CommandStreamHandle cmdStream, - const vkcv::ImageHandle motionBuffer, - const eMotionVectorMode debugView, - const float velocityRange) { + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBuffer, + const eMotionVectorVisualisationMode mode, + const float velocityRange) { computeMotionTiles(cmdStream, motionBuffer); vkcv::ImageHandle visualisationInput; - if ( debugView == eMotionVectorMode::FullResolution) + if ( mode == eMotionVectorVisualisationMode::FullResolution) visualisationInput = motionBuffer; - else if (debugView == eMotionVectorMode::MaxTile) + else if (mode == eMotionVectorVisualisationMode::MaxTile) visualisationInput = m_renderTargets.motionMax; - else if (debugView == eMotionVectorMode::MaxTileNeighbourhood) + else if (mode == eMotionVectorVisualisationMode::MaxTileNeighbourhood) visualisationInput = m_renderTargets.motionMaxNeighbourhood; + else if (mode == eMotionVectorVisualisationMode::MinTile) + visualisationInput = m_renderTargets.motionMin; + else if (mode == eMotionVectorVisualisationMode::MinTileNeighbourhood) + visualisationInput = m_renderTargets.motionMinNeighbourhood; + else if (mode == eMotionVectorVisualisationMode::None) { + vkcv_log(vkcv::LogLevel::ERROR, "renderMotionVectorVisualisation called with visualisation mode 'None'"); + return motionBuffer; + } else { vkcv_log(vkcv::LogLevel::ERROR, "Unknown eDebugView enum value"); return motionBuffer; @@ -336,19 +372,21 @@ void MotionBlur::computeMotionTiles( const vkcv::CommandStreamHandle cmdStream, const vkcv::ImageHandle motionBufferFullRes) { - // motion vector max tiles + // motion vector min max tiles vkcv::DescriptorWrites motionVectorMaxTilesDescriptorWrites; motionVectorMaxTilesDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, motionBufferFullRes) }; motionVectorMaxTilesDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, m_nearestSampler) }; motionVectorMaxTilesDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(2, m_renderTargets.motionMax) }; + vkcv::StorageImageDescriptorWrite(2, m_renderTargets.motionMax), + vkcv::StorageImageDescriptorWrite(3, m_renderTargets.motionMin) }; - m_core->writeDescriptorSet(m_motionVectorMaxPass.descriptorSet, motionVectorMaxTilesDescriptorWrites); + m_core->writeDescriptorSet(m_motionVectorMinMaxPass.descriptorSet, motionVectorMaxTilesDescriptorWrites); m_core->prepareImageForSampling(cmdStream, motionBufferFullRes); m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMax); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMin); const std::array<uint32_t, 3> motionTileDispatchCounts = computeFullscreenDispatchSize( m_core->getImageWidth( m_renderTargets.motionMax), @@ -357,29 +395,34 @@ void MotionBlur::computeMotionTiles( m_core->recordComputeDispatchToCmdStream( cmdStream, - m_motionVectorMaxPass.pipeline, + m_motionVectorMinMaxPass.pipeline, motionTileDispatchCounts.data(), - { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionVectorMaxPass.descriptorSet).vulkanHandle) }, + { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionVectorMinMaxPass.descriptorSet).vulkanHandle) }, vkcv::PushConstants(0)); - // motion vector max neighbourhood + // motion vector min max neighbourhood vkcv::DescriptorWrites motionVectorMaxNeighbourhoodDescriptorWrites; motionVectorMaxNeighbourhoodDescriptorWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionMax) }; + vkcv::SampledImageDescriptorWrite(0, m_renderTargets.motionMax), + vkcv::SampledImageDescriptorWrite(1, m_renderTargets.motionMin) }; motionVectorMaxNeighbourhoodDescriptorWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, m_nearestSampler) }; + vkcv::SamplerDescriptorWrite(2, m_nearestSampler) }; motionVectorMaxNeighbourhoodDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(2, m_renderTargets.motionMaxNeighbourhood) }; + vkcv::StorageImageDescriptorWrite(3, m_renderTargets.motionMaxNeighbourhood), + vkcv::StorageImageDescriptorWrite(4, m_renderTargets.motionMinNeighbourhood) }; - m_core->writeDescriptorSet(m_motionVectorMaxNeighbourhoodPass.descriptorSet, motionVectorMaxNeighbourhoodDescriptorWrites); + m_core->writeDescriptorSet(m_motionVectorMinMaxNeighbourhoodPass.descriptorSet, motionVectorMaxNeighbourhoodDescriptorWrites); m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMax); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMin); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMaxNeighbourhood); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMinNeighbourhood); m_core->recordComputeDispatchToCmdStream( cmdStream, - m_motionVectorMaxNeighbourhoodPass.pipeline, + m_motionVectorMinMaxNeighbourhoodPass.pipeline, motionTileDispatchCounts.data(), - { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionVectorMaxNeighbourhoodPass.descriptorSet).vulkanHandle) }, + { vkcv::DescriptorSetUsage(0, m_core->getDescriptorSet(m_motionVectorMinMaxNeighbourhoodPass.descriptorSet).vulkanHandle) }, vkcv::PushConstants(0)); } \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlur.hpp b/projects/indirect_dispatch/src/MotionBlur.hpp index 13eff13b..b50f0af6 100644 --- a/projects/indirect_dispatch/src/MotionBlur.hpp +++ b/projects/indirect_dispatch/src/MotionBlur.hpp @@ -4,16 +4,22 @@ #include "MotionBlurSetup.hpp" // selection for motion blur input and visualisation -enum class eMotionVectorMode : int { - FullResolution = 0, - MaxTile = 1, - MaxTileNeighbourhood = 2, - OptionCount = 3 }; +enum class eMotionVectorVisualisationMode : int { + None = 0, + FullResolution = 1, + MaxTile = 2, + MaxTileNeighbourhood = 3, + MinTile = 4, + MinTileNeighbourhood = 5, + OptionCount = 6 }; -static const char* MotionVectorModeLabels[3] = { +static const char* MotionVectorVisualisationModeLabels[6] = { + "None", "Full resolution", "Max tile", - "Tile neighbourhood max" }; + "Tile neighbourhood max", + "Min Tile", + "Tile neighbourhood min"}; enum class eMotionBlurMode : int { Default = 0, @@ -37,19 +43,19 @@ public: const vkcv::ImageHandle motionBufferFullRes, const vkcv::ImageHandle colorBuffer, const vkcv::ImageHandle depthBuffer, - const eMotionVectorMode motionVectorMode, const eMotionBlurMode mode, const float cameraNear, const float cameraFar, const float deltaTimeSeconds, const float cameraShutterSpeedInverse, - const float motionTileOffsetLength); + const float motionTileOffsetLength, + const float fastPathThreshold); vkcv::ImageHandle renderMotionVectorVisualisation( - const vkcv::CommandStreamHandle cmdStream, - const vkcv::ImageHandle motionBuffer, - const eMotionVectorMode debugView, - const float velocityRange); + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBuffer, + const eMotionVectorVisualisationMode mode, + const float velocityRange); private: // computes max per tile and neighbourhood tile max @@ -63,14 +69,16 @@ private: vkcv::SamplerHandle m_nearestSampler; ComputePassHandles m_motionBlurPass; - ComputePassHandles m_motionVectorMaxPass; - ComputePassHandles m_motionVectorMaxNeighbourhoodPass; + ComputePassHandles m_motionVectorMinMaxPass; + ComputePassHandles m_motionVectorMinMaxNeighbourhoodPass; ComputePassHandles m_motionVectorVisualisationPass; ComputePassHandles m_colorCopyPass; ComputePassHandles m_tileClassificationPass; ComputePassHandles m_tileResetPass; ComputePassHandles m_tileVisualisationPass; + ComputePassHandles m_motionBlurFastPathPass; vkcv::BufferHandle m_fullPathWorkTileBuffer; vkcv::BufferHandle m_copyPathWorkTileBuffer; + vkcv::BufferHandle m_fastPathWorkTileBuffer; }; \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.cpp b/projects/indirect_dispatch/src/MotionBlurSetup.cpp index 2ac1e7d1..82d2593a 100644 --- a/projects/indirect_dispatch/src/MotionBlurSetup.cpp +++ b/projects/indirect_dispatch/src/MotionBlurSetup.cpp @@ -27,6 +27,22 @@ MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t false, true).getHandle(); + targets.motionMin = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true).getHandle(); + + targets.motionMinNeighbourhood = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + motionMaxWidth, + motionMaxheight, + 1, + false, + true).getHandle(); + targets.outputColor = core.createImage( MotionBlurConfig::outputColorFormat, width, diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.hpp b/projects/indirect_dispatch/src/MotionBlurSetup.hpp index 9c104ce7..ca169d7c 100644 --- a/projects/indirect_dispatch/src/MotionBlurSetup.hpp +++ b/projects/indirect_dispatch/src/MotionBlurSetup.hpp @@ -5,6 +5,8 @@ struct MotionBlurRenderTargets { vkcv::ImageHandle outputColor; vkcv::ImageHandle motionMax; vkcv::ImageHandle motionMaxNeighbourhood; + vkcv::ImageHandle motionMin; + vkcv::ImageHandle motionMinNeighbourhood; }; namespace MotionBlurSetup { -- GitLab