Browse Source

Feed tonemapped data to bloom pyramid and revert tonemapping in the 1st pass

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
1e4834ec3a

+ 8 - 0
AnKi/Renderer/Bloom.cpp

@@ -15,6 +15,7 @@ namespace anki {
 Bloom::Bloom(Renderer* r)
 	: RendererObject(r)
 {
+	registerDebugRenderTarget("Bloom");
 }
 
 Bloom::~Bloom()
@@ -216,4 +217,11 @@ void Bloom::populateRenderGraph(RenderingContext& ctx)
 	}
 }
 
+void Bloom::getDebugRenderTarget([[maybe_unused]] CString rtName, RenderTargetHandle& handle,
+								 [[maybe_unused]] ShaderProgramPtr& optionalShaderProgram) const
+{
+	ANKI_ASSERT(rtName == "Bloom");
+	handle = m_runCtx.m_upscaleRt;
+}
+
 } // end namespace anki

+ 3 - 0
AnKi/Renderer/Bloom.h

@@ -83,6 +83,9 @@ private:
 	Error initUpscale();
 
 	Error initInternal();
+
+	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle,
+							  [[maybe_unused]] ShaderProgramPtr& optionalShaderProgram) const override;
 };
 
 /// @}

+ 12 - 8
AnKi/Renderer/DownscaleBlur.cpp

@@ -5,7 +5,8 @@
 
 #include <AnKi/Renderer/DownscaleBlur.h>
 #include <AnKi/Renderer/Renderer.h>
-#include <AnKi/Renderer/TemporalAA.h>
+#include <AnKi/Renderer/Scale.h>
+#include <AnKi/Renderer/Tonemapping.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -113,7 +114,7 @@ void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 				TextureSubresourceInfo renderSubresource;
 
 				pass.newDependency({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE, renderSubresource});
-				pass.newDependency({m_r->getTemporalAA().getHdrRt(), TextureUsageBit::SAMPLED_COMPUTE});
+				pass.newDependency({m_r->getScale().getRt(), TextureUsageBit::SAMPLED_COMPUTE});
 			}
 		}
 	}
@@ -143,7 +144,7 @@ void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 				TextureSubresourceInfo renderSubresource;
 
 				pass.newDependency({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, renderSubresource});
-				pass.newDependency({m_r->getTemporalAA().getHdrRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+				pass.newDependency({m_r->getScale().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 			}
 		}
 	}
@@ -168,17 +169,20 @@ void DownscaleBlur::run(U32 passIdx, RenderPassWorkContext& rgraphCtx)
 	}
 	else
 	{
-		rgraphCtx.bindColorTexture(0, 1, m_r->getTemporalAA().getHdrRt());
+		rgraphCtx.bindColorTexture(0, 1, m_r->getScale().getRt());
 	}
 
+	rgraphCtx.bindUniformBuffer(0, 2, m_r->getTonemapping().getAverageLuminanceBuffer());
+
+	const Bool revertTonemap = passIdx == 0;
+	const UVec4 fbSize(vpWidth, vpHeight, revertTonemap, 0);
+	cmdb->setPushConstants(&fbSize, sizeof(fbSize));
+
 	if(getConfig().getRPreferCompute())
 	{
 		TextureSubresourceInfo sampleSubresource;
 		sampleSubresource.m_firstMipmap = passIdx;
-		rgraphCtx.bindImage(0, 2, m_runCtx.m_rt, sampleSubresource);
-
-		UVec4 fbSize(vpWidth, vpHeight, 0, 0);
-		cmdb->setPushConstants(&fbSize, sizeof(fbSize));
+		rgraphCtx.bindImage(0, 3, m_runCtx.m_rt, sampleSubresource);
 
 		dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], vpWidth, vpHeight);
 	}

+ 10 - 14
AnKi/Renderer/Scale.cpp

@@ -114,8 +114,8 @@ void Scale::populateRenderGraph(RenderingContext& ctx)
 {
 	if(!doScaling() && !doSharpening())
 	{
-		m_runCtx.m_scaledRt = m_r->getTemporalAA().getTonemappedRt();
-		m_runCtx.m_sharpenedRt = m_r->getTemporalAA().getTonemappedRt();
+		m_runCtx.m_scaledRt = m_r->getTemporalAA().getRt();
+		m_runCtx.m_sharpenedRt = m_r->getTemporalAA().getRt();
 		return;
 	}
 
@@ -129,8 +129,7 @@ void Scale::populateRenderGraph(RenderingContext& ctx)
 		if(preferCompute)
 		{
 			ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Scale");
-			pass.newDependency(
-				RenderPassDependency(m_r->getTemporalAA().getTonemappedRt(), TextureUsageBit::SAMPLED_COMPUTE));
+			pass.newDependency(RenderPassDependency(m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_COMPUTE));
 			pass.newDependency(RenderPassDependency(m_runCtx.m_scaledRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
 
 			pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
@@ -142,8 +141,7 @@ void Scale::populateRenderGraph(RenderingContext& ctx)
 			GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Scale");
 			pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_scaledRt});
 
-			pass.newDependency(
-				RenderPassDependency(m_r->getTemporalAA().getTonemappedRt(), TextureUsageBit::SAMPLED_FRAGMENT));
+			pass.newDependency(RenderPassDependency(m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_FRAGMENT));
 			pass.newDependency(
 				RenderPassDependency(m_runCtx.m_scaledRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
 
@@ -160,9 +158,8 @@ void Scale::populateRenderGraph(RenderingContext& ctx)
 		if(preferCompute)
 		{
 			ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Sharpen");
-			pass.newDependency(
-				RenderPassDependency((!doScaling()) ? m_r->getTemporalAA().getTonemappedRt() : m_runCtx.m_scaledRt,
-									 TextureUsageBit::SAMPLED_COMPUTE));
+			pass.newDependency(RenderPassDependency((!doScaling()) ? m_r->getTemporalAA().getRt() : m_runCtx.m_scaledRt,
+													TextureUsageBit::SAMPLED_COMPUTE));
 			pass.newDependency(RenderPassDependency(m_runCtx.m_sharpenedRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
 
 			pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
@@ -174,9 +171,8 @@ void Scale::populateRenderGraph(RenderingContext& ctx)
 			GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Sharpen");
 			pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_sharpenedRt});
 
-			pass.newDependency(
-				RenderPassDependency((!doScaling()) ? m_r->getTemporalAA().getTonemappedRt() : m_runCtx.m_scaledRt,
-									 TextureUsageBit::SAMPLED_FRAGMENT));
+			pass.newDependency(RenderPassDependency((!doScaling()) ? m_r->getTemporalAA().getRt() : m_runCtx.m_scaledRt,
+													TextureUsageBit::SAMPLED_FRAGMENT));
 			pass.newDependency(
 				RenderPassDependency(m_runCtx.m_sharpenedRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
 
@@ -195,7 +191,7 @@ void Scale::runScaling(RenderPassWorkContext& rgraphCtx)
 	cmdb->bindShaderProgram(m_scaleGrProg);
 
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
-	rgraphCtx.bindColorTexture(0, 1, m_r->getTemporalAA().getTonemappedRt());
+	rgraphCtx.bindColorTexture(0, 1, m_r->getTemporalAA().getRt());
 
 	if(preferCompute)
 	{
@@ -257,7 +253,7 @@ void Scale::runSharpening(RenderPassWorkContext& rgraphCtx)
 	cmdb->bindShaderProgram(m_sharpenGrProg);
 
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
-	rgraphCtx.bindColorTexture(0, 1, (!doScaling()) ? m_r->getTemporalAA().getTonemappedRt() : m_runCtx.m_scaledRt);
+	rgraphCtx.bindColorTexture(0, 1, (!doScaling()) ? m_r->getTemporalAA().getRt() : m_runCtx.m_scaledRt);
 
 	if(preferCompute)
 	{

+ 1 - 1
AnKi/Renderer/TemporalAA.cpp

@@ -44,7 +44,7 @@ Error TemporalAA::initInternal()
 
 	{
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
-		variantInitInfo.addConstant("VARIANCE_CLIPPING_GAMMA", 2.7f);
+		variantInitInfo.addConstant("VARIANCE_CLIPPING_GAMMA", 2.7f); // Variance clipping paper proposes 1.0
 		variantInitInfo.addConstant("BLEND_FACTOR", 1.0f / 16.0f);
 		variantInitInfo.addMutation("VARIANCE_CLIPPING", 1);
 		variantInitInfo.addMutation("YCBCR", 0);

+ 2 - 6
AnKi/Renderer/TemporalAA.h

@@ -24,12 +24,8 @@ public:
 
 	void populateRenderGraph(RenderingContext& ctx);
 
-	RenderTargetHandle getHdrRt() const
-	{
-		return m_runCtx.m_renderRt;
-	}
-
-	RenderTargetHandle getTonemappedRt() const
+	/// Result is tonemaped.
+	RenderTargetHandle getRt() const
 	{
 		return m_runCtx.m_tonemappedRt;
 	}

+ 19 - 7
AnKi/Shaders/DownscaleBlur.glsl

@@ -5,24 +5,29 @@
 
 #pragma once
 
-#include <AnKi/Shaders/Common.glsl>
+#include <AnKi/Shaders/TonemappingFunctions.glsl>
+#include <AnKi/Shaders/Functions.glsl>
 
 layout(set = 0, binding = 0) uniform sampler u_linearAnyClampSampler;
 layout(set = 0, binding = 1) uniform ANKI_RP texture2D u_tex;
 
-#if defined(ANKI_COMPUTE_SHADER)
-const UVec2 WORKGROUP_SIZE = UVec2(16, 16);
-layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+const U32 TONEMAPPING_SET = 0u;
+const U32 TONEMAPPING_BINDING = 2u;
+#include <AnKi/Shaders/TonemappingResources.glsl>
 
-// Push constants hold the size of the output image
 layout(push_constant, row_major, std140) uniform b_pc
 {
 	UVec2 u_fbSize;
-	UVec2 u_padding;
+	U32 u_revertTonemapping;
+	U32 u_padding;
 };
 
+#if defined(ANKI_COMPUTE_SHADER)
+const UVec2 WORKGROUP_SIZE = UVec2(16, 16);
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
 Vec2 in_uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(u_fbSize);
-layout(set = 0, binding = 2) writeonly uniform ANKI_RP image2D out_img;
+layout(set = 0, binding = 3) writeonly uniform ANKI_RP image2D out_img;
 ANKI_RP Vec3 out_color;
 #else
 layout(location = 0) in Vec2 in_uv;
@@ -46,6 +51,13 @@ void main()
 	out_color += textureLodOffset(sampler2D(u_tex, u_linearAnyClampSampler), in_uv, 0.0, IVec2(+1, -1)).rgb * weight;
 	out_color += textureLodOffset(sampler2D(u_tex, u_linearAnyClampSampler), in_uv, 0.0, IVec2(-1, +1)).rgb * weight;
 
+	if(u_revertTonemapping != 0u)
+	{
+		out_color = saturate(out_color);
+		out_color = sRgbToLinear(out_color);
+		out_color = invertTonemap(out_color, u_exposureThreshold0);
+	}
+
 #if defined(ANKI_COMPUTE_SHADER)
 	imageStore(out_img, IVec2(gl_GlobalInvocationID.xy), Vec4(out_color, 0.0));
 #endif

+ 2 - 2
AnKi/Shaders/PackFunctions.glsl

@@ -151,7 +151,7 @@ void packGBuffer(GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2, out Ve
 {
 	const F32 packedSubsurfaceMetallic = packUnorm2ToUnorm1(Vec2(g.m_subsurface, g.m_metallic));
 
-	const Vec3 tonemappedEmission = invertibleTonemap(g.m_emission);
+	const Vec3 tonemappedEmission = invertReinhardTonemap(g.m_emission);
 
 	rt0 = Vec4(g.m_diffuse, packedSubsurfaceMetallic);
 	rt1 = Vec4(g.m_roughness, g.m_f0.x, tonemappedEmission.rb);
@@ -189,7 +189,7 @@ void unpackGBufferNoVelocity(ANKI_RP Vec4 rt0, ANKI_RP Vec4 rt1, ANKI_RP Vec4 rt
 
 	g.m_roughness = unpackRoughnessFromGBuffer(rt1);
 	g.m_f0 = Vec3(rt1.y);
-	g.m_emission = invertInvertibleTonemap(Vec3(rt1.z, rt2.x, rt1.w));
+	g.m_emission = invertReinhardTonemap(Vec3(rt1.z, rt2.x, rt1.w));
 
 	g.m_normal = signedOctDecode(rt2.yzw);
 

+ 2 - 2
AnKi/Shaders/TemporalAA.glsl

@@ -95,8 +95,8 @@ void main()
 	const F32 lum1 = historyCol.r;
 	const F32 maxLum = boxMax.r;
 #else
-	const F32 lum0 = computeLuminance(invertibleTonemap(crntCol));
-	const F32 lum1 = computeLuminance(invertibleTonemap(historyCol));
+	const F32 lum0 = computeLuminance(reinhardTonemap(crntCol));
+	const F32 lum1 = computeLuminance(reinhardTonemap(historyCol));
 	const F32 maxLum = 1.0;
 #endif
 

+ 39 - 10
AnKi/Shaders/TonemappingFunctions.glsl

@@ -65,15 +65,28 @@ ANKI_RP Vec3 tonemapACESFilm(ANKI_RP Vec3 x)
 	return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
 }
 
+ANKI_RP Vec3 invertTonemapACESFilm(ANKI_RP Vec3 x)
+{
+	const ANKI_RP F32 a = 2.51;
+	const ANKI_RP F32 b = 0.03;
+	const ANKI_RP F32 c = 2.43;
+	const ANKI_RP F32 d = 0.59;
+	const ANKI_RP F32 e = 0.14;
+
+	return (-0.59 * x + 0.03 - sqrt(-1.0127 * x * x + 1.3702 * x + 0.0009)) / (2.0 * (2.43 * x - 2.51));
+}
+
 ANKI_RP Vec3 tonemap(ANKI_RP Vec3 color, ANKI_RP F32 exposure)
 {
 	color *= exposure;
-#if 0
-	const ANKI_RP F32 saturation = 1.0;
-	return tonemapReinhard(color, saturation);
-#else
 	return tonemapACESFilm(color);
-#endif
+}
+
+ANKI_RP Vec3 invertTonemap(ANKI_RP Vec3 color, ANKI_RP F32 exposure)
+{
+	color = invertTonemapACESFilm(color);
+	color /= max(EPSILON, exposure);
+	return color;
 }
 
 ANKI_RP Vec3 tonemap(ANKI_RP Vec3 color, ANKI_RP F32 avgLum, ANKI_RP F32 threshold)
@@ -83,14 +96,30 @@ ANKI_RP Vec3 tonemap(ANKI_RP Vec3 color, ANKI_RP F32 avgLum, ANKI_RP F32 thresho
 }
 
 // https://graphicrants.blogspot.com/2013/12/tone-mapping.html
-ANKI_RP Vec3 invertibleTonemap(ANKI_RP Vec3 colour)
+Vec3 reinhardTonemap(Vec3 colour)
 {
-	// 1 / (1 + max(rgb))
+	// rgb / (1 + max(rgb))
 	return colour / (1.0 + max(max(colour.r, colour.g), colour.b));
 }
 
-ANKI_RP Vec3 invertInvertibleTonemap(ANKI_RP Vec3 colour)
+F32 reinhardTonemap(F32 value)
+{
+	return value / (1.0 + value);
+}
+
+F16 reinhardTonemap(F16 value)
+{
+	return value / (1.0hf + value);
+}
+
+Vec3 invertReinhardTonemap(Vec3 colour)
+{
+	// rgb / (1 - max(rgb))
+	return colour / max(1.0 / 32768.0, 1.0 - max(max(colour.r, colour.g), colour.b));
+}
+
+HVec3 invertReinhardTonemap(HVec3 colour)
 {
-	// 1 / (1 - max(rgb))
-	return colour / (1.0 - max(max(colour.r, colour.g), colour.b));
+	// rgb / (1 - max(rgb))
+	return colour / max(F16(1.0 / 32768.0), 1.0hf - max(max(colour.r, colour.g), colour.b));
 }

+ 1 - 1
AnKi/Shaders/VisualizeHdrRenderTarget.ankiprog

@@ -18,6 +18,6 @@ layout(location = 0) out Vec3 out_color;
 
 void main()
 {
-	out_color = invertibleTonemap(textureLod(u_inTex, u_nearestAnyClampSampler, in_uv, 0.0).rgb);
+	out_color = reinhardTonemap(textureLod(u_inTex, u_nearestAnyClampSampler, in_uv, 0.0).rgb);
 }
 #pragma anki end

+ 1 - 2
Samples/PhysicsPlayground/Main.cpp

@@ -249,8 +249,7 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 
 	if(getInput().getKey(KeyCode::L) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget(
-			(renderer.getCurrentDebugRenderTarget() == "LightShading") ? "" : "LightShading");
+		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "Bloom") ? "" : "Bloom");
 	}
 
 	if(getInput().getKey(KeyCode::J) == 1)