From 47c794626fb0f8a5f1d424e9e9b55803a060d4c9 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Wed, 18 Aug 2021 17:12:31 +0200
Subject: [PATCH] [#106] Add jitter to motion tile lookup in motion blur to
 replace hard edges in blur with noise

---
 .../resources/shaders/motionBlur.comp         | 21 ++++++++++++++++++-
 projects/indirect_dispatch/src/App.cpp        |  7 +++++--
 projects/indirect_dispatch/src/MotionBlur.cpp | 11 ++++++----
 projects/indirect_dispatch/src/MotionBlur.hpp |  3 ++-
 4 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/projects/indirect_dispatch/resources/shaders/motionBlur.comp b/projects/indirect_dispatch/resources/shaders/motionBlur.comp
index 43981689..d340d4d4 100644
--- a/projects/indirect_dispatch/resources/shaders/motionBlur.comp
+++ b/projects/indirect_dispatch/resources/shaders/motionBlur.comp
@@ -17,6 +17,7 @@ layout( push_constant ) uniform constants{
     // camera planes are needed to linearize depth
     float cameraNearPlane;
     float cameraFarPlane;
+    float motionTileOffsetLength;
 };
 
 float linearizeDepth(float depth, float near, float far){
@@ -111,6 +112,22 @@ float dither(ivec2 coord){
     return x ^^ y ? 1 : 0;
 }
 
+// from https://www.shadertoy.com/view/ttc3zr
+uvec2 murmurHash22(uvec2 src) {
+    const uint M = 0x5bd1e995u;
+    uvec2 h = uvec2(1190494759u, 2147483647u);
+    src *= M; src ^= src>>24u; src *= M;
+    h *= M; h ^= src.x; h *= M; h ^= src.y;
+    h ^= h>>13u; h *= M; h ^= h>>15u;
+    return h;
+}
+
+vec2 hash22(vec2 src) {
+    uvec2 h = murmurHash22(floatBitsToUint(src));
+    return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
+}
+
+
 void main(){
 
     if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage))))
@@ -120,7 +137,9 @@ void main(){
     ivec2   coord       = ivec2(gl_GlobalInvocationID.xy);
     vec2    uv          = vec2(coord + 0.5) / textureRes;   // + 0.5 to shift uv into pixel center
     
-    vec2 motionNeighbourhoodMax = processMotionVector(texture(sampler2D(inMotionNeighbourhoodMax, nearestSampler), uv).rg);
+    // the motion tile lookup is jittered, so the hard edges in the blur are replaced by noise
+    vec2 motionOffset           = motionTileOffsetLength * (hash22(coord) * 2 - 1) / textureRes;
+    vec2 motionNeighbourhoodMax = processMotionVector(texture(sampler2D(inMotionNeighbourhoodMax, nearestSampler), uv + motionOffset).rg);
     
     SampleData mainPixel = loadSampleData(uv);
     
diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp
index db97d57c..ecdbd50e 100644
--- a/projects/indirect_dispatch/src/App.cpp
+++ b/projects/indirect_dispatch/src/App.cpp
@@ -96,7 +96,8 @@ void App::run() {
 	eMotionVectorVisualisationMode  motionVectorVisualisationMode   = eMotionVectorVisualisationMode::None;
 	eMotionVectorMode               motionBlurMotionMode            = eMotionVectorMode::MaxTileNeighbourhood;
 
-    bool    freezeFrame                     = false;
+	bool    freezeFrame                     = false;
+	float   motionBlurTileOffsetLength      = 10;
 	float   objectVerticalSpeed             = 5;
 	float   objectAmplitude                 = 0;
 	float   objectMeanHeight                = 1;
@@ -276,7 +277,8 @@ void App::run() {
 				cameraNear,
 				cameraFar,
 				fDeltaTimeSeconds,
-				cameraShutterSpeedInverse);
+				cameraShutterSpeedInverse,
+				motionBlurTileOffsetLength);
 		}
 		else {
 			eMotionVectorMode debugViewMode;
@@ -331,6 +333,7 @@ void App::run() {
 		ImGui::Begin("Settings");
 
 		ImGui::Checkbox("Freeze frame", &freezeFrame);
+		ImGui::InputFloat("Motion tile offset length", &motionBlurTileOffsetLength);
 
 		ImGui::Combo(
 			"Debug view",
diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp
index a731b8b9..49e8b288 100644
--- a/projects/indirect_dispatch/src/MotionBlur.cpp
+++ b/projects/indirect_dispatch/src/MotionBlur.cpp
@@ -58,7 +58,8 @@ vkcv::ImageHandle MotionBlur::render(
 	const float                     cameraNear,
 	const float                     cameraFar,
 	const float                     deltaTimeSeconds,
-	const float                     cameraShutterSpeedInverse) {
+	const float                     cameraShutterSpeedInverse,
+	const float                     motionTileOffsetLength) {
 
 	computeMotionTiles(cmdStream, motionBufferFullRes);
 
@@ -93,14 +94,16 @@ vkcv::ImageHandle MotionBlur::render(
 		float motionFactor;
 		float cameraNearPlane;
 		float cameraFarPlane;
+		float motionTileOffsetLength;
 	};
 	MotionBlurConstantData motionBlurConstantData;
 
 	const float deltaTimeMotionBlur = deltaTimeSeconds;
 
-	motionBlurConstantData.motionFactor     = 1 / (deltaTimeMotionBlur * cameraShutterSpeedInverse);
-	motionBlurConstantData.cameraNearPlane  = cameraNear;
-	motionBlurConstantData.cameraFarPlane   = cameraFar;
+	motionBlurConstantData.motionFactor             = 1 / (deltaTimeMotionBlur * cameraShutterSpeedInverse);
+	motionBlurConstantData.cameraNearPlane          = cameraNear;
+	motionBlurConstantData.cameraFarPlane           = cameraFar;
+	motionBlurConstantData.motionTileOffsetLength   = motionTileOffsetLength;
 
 	vkcv::PushConstants motionBlurPushConstants(sizeof(motionBlurConstantData));
 	motionBlurPushConstants.appendDrawcall(motionBlurConstantData);
diff --git a/projects/indirect_dispatch/src/MotionBlur.hpp b/projects/indirect_dispatch/src/MotionBlur.hpp
index a6232125..b90cdfa4 100644
--- a/projects/indirect_dispatch/src/MotionBlur.hpp
+++ b/projects/indirect_dispatch/src/MotionBlur.hpp
@@ -31,7 +31,8 @@ public:
 		const float                     cameraNear,
 		const float                     cameraFar,
 		const float                     deltaTimeSeconds,
-		const float                     cameraShutterSpeedInverse);
+		const float                     cameraShutterSpeedInverse,
+		const float                     motionTileOffsetLength);
 
 	vkcv::ImageHandle renderMotionVectorVisualisation(
 		const vkcv::CommandStreamHandle cmdStream,
-- 
GitLab