From 7aded7d53366442941272da756b64b9d52a29a2b Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Tue, 17 Aug 2021 15:49:04 +0200
Subject: [PATCH] [#106] Correct handling of motion blur motion vectors
 according to paper

---
 .../resources/shaders/motionBlur.comp         | 26 +++++++++++++------
 .../resources/shaders/motionBlurConfig.inc    |  1 +
 .../resources/shaders/motionVectorMax.comp    |  3 +--
 projects/indirect_dispatch/src/App.cpp        |  2 +-
 4 files changed, 21 insertions(+), 11 deletions(-)
 create mode 100644 projects/indirect_dispatch/resources/shaders/motionBlurConfig.inc

diff --git a/projects/indirect_dispatch/resources/shaders/motionBlur.comp b/projects/indirect_dispatch/resources/shaders/motionBlur.comp
index 62bae130..5c196016 100644
--- a/projects/indirect_dispatch/resources/shaders/motionBlur.comp
+++ b/projects/indirect_dispatch/resources/shaders/motionBlur.comp
@@ -1,5 +1,6 @@
 #version 440
 #extension GL_GOOGLE_include_directive : enable
+#include "motionBlurConfig.inc"
 
 layout(set=0, binding=0)                    uniform texture2D   inColor;
 layout(set=0, binding=1)                    uniform texture2D   inDepth;
@@ -10,7 +11,7 @@ layout(set=0, binding=4, r11f_g11f_b10f)    uniform image2D     outImage;
 layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
 
 layout( push_constant ) uniform constants{
-    float motionFactor;  // computed from delta time and shutter speed
+    float motionScaleFactor;  // computed from delta time and shutter speed
     float minVelocity;
     // camera planes are needed to linearize depth
     float cameraNearPlane;
@@ -64,11 +65,26 @@ float computeSampleWeigth(SampleData mainPixel, SampleData samplePixel){
     return weight;
 }
 
+// see "A Reconstruction Filter for Plausible Motion Blur", section 2.2
+vec2 rescaleMotion(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
+    
+    // pixels are anisotropic so the smaller dimension is used, so the clamping is conservative
+    float pixelSize     = 1.f / max(imageSize(outImage).x, imageSize(outImage).y);
+    float velocity      = length(motionScaled);
+    float epsilon       = 0.0001;
+    // this clamps the motion to not exceed the radius given by the motion tile size
+    return motionScaled * max(0.5 * pixelSize, min(velocity, motionTileSize * pixelSize)) / (velocity + epsilon);
+}
+
 SampleData loadSampleData(vec2 uv){
     
     SampleData data;
     data.uv             = uv;
-    data.motion         = texture(sampler2D(inMotion, nearestSampler), uv).rg * motionFactor;
+    data.motion         = rescaleMotion(texture(sampler2D(inMotion, nearestSampler), uv).rg);
     data.velocity       = length(data.motion);
     data.depthLinear    = texture(sampler2D(inDepth, nearestSampler), uv).r;
     data.depthLinear    = linearizeDepth(data.depthLinear, cameraNearPlane, cameraFarPlane);
@@ -105,12 +121,6 @@ void main(){
         imageStore(outImage, coord, vec4(color, 0.f));
         return;
     }
-    
-    // TODO: check if a max velocity is necessary
-    // // TODO: should be configurable by user or computed by velocity tile sizes
-    // const float maxBlurDistance = 0.075;
-    // if(mainPixel.velocity > maxBlurDistance)
-    //     motion *= maxBlurDistance / velocity;
 
     vec3        color       = vec3(0);
     float       weightSum   = 0;
diff --git a/projects/indirect_dispatch/resources/shaders/motionBlurConfig.inc b/projects/indirect_dispatch/resources/shaders/motionBlurConfig.inc
new file mode 100644
index 00000000..fdd915c8
--- /dev/null
+++ b/projects/indirect_dispatch/resources/shaders/motionBlurConfig.inc
@@ -0,0 +1 @@
+const int motionTileSize = 20;
\ No newline at end of file
diff --git a/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp b/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp
index 725fc3f4..65a6186c 100644
--- a/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp
+++ b/projects/indirect_dispatch/resources/shaders/motionVectorMax.comp
@@ -1,5 +1,6 @@
 #version 440
 #extension GL_GOOGLE_include_directive : enable
+#include "motionBlurConfig.inc"
 
 layout(set=0, binding=0)        uniform texture2D   inMotion;
 layout(set=0, binding=1)        uniform sampler     textureSampler;
@@ -7,8 +8,6 @@ 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 = 20;
-
 void main(){
     
     ivec2 outImageRes       = imageSize(outMotionMax);
diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp
index 88fdca59..3b9992ed 100644
--- a/projects/indirect_dispatch/src/App.cpp
+++ b/projects/indirect_dispatch/src/App.cpp
@@ -89,7 +89,7 @@ void App::run() {
 
 	float   objectVerticalSpeed             = 5;
 	float   motionBlurMinVelocity           = 0.001;
-	int     cameraShutterSpeedInverse       = 30;
+	int     cameraShutterSpeedInverse       = 24;
 	float   motionVectorVisualisationRange  = 0.008;
 
 	glm::mat4 mvpPrevious               = glm::mat4(1.f);
-- 
GitLab