From a7bdc39ab26c8cda3965911fe1653f2a384ba915 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Fri, 25 Jun 2021 13:33:00 +0200
Subject: [PATCH] [#82] Add subtle film grain

---
 .../resources/shaders/tonemapping.comp        | 56 ++++++++++++++++++-
 projects/voxelization/src/main.cpp            | 20 ++++++-
 2 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/projects/voxelization/resources/shaders/tonemapping.comp b/projects/voxelization/resources/shaders/tonemapping.comp
index 60830169..b1bb08de 100644
--- a/projects/voxelization/resources/shaders/tonemapping.comp
+++ b/projects/voxelization/resources/shaders/tonemapping.comp
@@ -3,9 +3,12 @@
 layout(set=0, binding=0, r11f_g11f_b10f)    uniform image2D inImage;
 layout(set=0, binding=1, rgba8)             uniform image2D outImage;
 
-
 layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
 
+layout( push_constant ) uniform constants{
+    float time;
+};
+
 // from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
 vec3 ACESFilm(vec3 x)
 {
@@ -17,6 +20,55 @@ vec3 ACESFilm(vec3 x)
     return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1);
 }
 
+// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW.
+float hash(vec3 p3){
+    p3 = fract(p3 * 0.1031);
+    p3 += dot(p3,p3.yzx + 19.19);
+    return fract((p3.x + p3.y) * p3.z);
+}
+
+// From iq: https://www.shadertoy.com/view/4sfGzS.
+float noise(vec3 x){
+    vec3 i = floor(x);
+    vec3 f = fract(x);
+    f = f*f*(3.0-2.0*f);
+    return mix(mix(mix(hash(i+vec3(0, 0, 0)), 
+                       hash(i+vec3(1, 0, 0)),f.x),
+                   mix(hash(i+vec3(0, 1, 0)), 
+                       hash(i+vec3(1, 1, 0)),f.x),f.y),
+               mix(mix(hash(i+vec3(0, 0, 1)), 
+                       hash(i+vec3(1, 0, 1)),f.x),
+                   mix(hash(i+vec3(0, 1, 1)), 
+                       hash(i+vec3(1, 1, 1)),f.x),f.y),f.z);
+}
+
+// From: https://www.shadertoy.com/view/3sGSWVF
+// Slightly high-passed continuous value-noise.
+float grainSource(vec3 x, float strength, float pitch){
+    float center = noise(x);
+	float v1 = center - noise(vec3( 1, 0, 0)/pitch + x) + 0.5;
+	float v2 = center - noise(vec3( 0, 1, 0)/pitch + x) + 0.5;
+	float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5;
+	float v4 = center - noise(vec3( 0,-1, 0)/pitch + x) + 0.5;
+    
+	float total = (v1 + v2 + v3 + v4) / 4.0;
+	return mix(1, 0.5 + total, strength);
+}
+
+vec3 applyGrain(ivec2 uv, vec3 c){
+    float grainLift     = 0.6;
+    float grainStrength = 0.4;
+    float grainTimeFactor = 0.1;
+    
+    float timeColorOffset = 1.2;
+    vec3 grain = vec3(
+        grainSource(vec3(uv, floor(grainTimeFactor*time)),                   grainStrength, grainLift),
+        grainSource(vec3(uv, floor(grainTimeFactor*time + timeColorOffset)), grainStrength, grainLift),
+        grainSource(vec3(uv, floor(grainTimeFactor*time - timeColorOffset)), grainStrength, grainLift));
+    
+    return c * grain;
+}
+
 void main(){
 
     if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){
@@ -25,6 +77,8 @@ void main(){
     ivec2 uv            = ivec2(gl_GlobalInvocationID.xy);
     vec3 linearColor    = imageLoad(inImage, uv).rgb;
     vec3 tonemapped     = ACESFilm(linearColor);
+    tonemapped          = applyGrain(uv, tonemapped);
+    
     vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f));
     imageStore(outImage, uv, vec4(gammaCorrected, 0.f));
 }
\ No newline at end of file
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 5fc41cdc..f141538d 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -645,12 +645,15 @@ int main(int argc, const char** argv) {
 		core.prepareImageForStorage(cmdStream, swapchainInput);
 		core.prepareImageForStorage(cmdStream, resolvedColorBuffer);
 
+		auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime);
+		float timeF         = static_cast<float>(timeSinceStart.count()) * 0.01;
+
 		core.recordComputeDispatchToCmdStream(
 			cmdStream, 
 			tonemappingPipeline, 
 			fulsscreenDispatchCount,
 			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) },
-			vkcv::PushConstantData(nullptr, 0));
+			vkcv::PushConstantData(&timeF, sizeof(timeF)));
 
 		// present and end
 		core.prepareSwapchainImageForPresent(cmdStream);
@@ -702,6 +705,21 @@ int main(int argc, const char** argv) {
 				forwardPipeline = newPipeline;
 			}
 		}
+		if (ImGui::Button("Reload tonemapping")) {
+
+			vkcv::ShaderProgram newProgram;
+			compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/tonemapping.comp"),
+				[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+				newProgram.addShader(shaderStage, path);
+			});
+			vkcv::PipelineHandle newPipeline = core.createComputePipeline(
+				newProgram, 
+				{ core.getDescriptorSet(tonemappingDescriptorSet).layout });
+
+			if (newPipeline) {
+				tonemappingPipeline = newPipeline;
+			}
+		}
 
 		ImGui::End();
 
-- 
GitLab