Parcourir la source

Tempora denoising on reflections

Panagiotis Christopoulos Charitos il y a 3 ans
Parent
commit
68d757ec16

+ 41 - 13
AnKi/Renderer/IndirectSpecular.cpp

@@ -10,6 +10,7 @@
 #include <AnKi/Renderer/DownscaleBlur.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/ProbeReflections.h>
+#include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -32,14 +33,21 @@ Error IndirectSpecular::initInternal()
 {
 	const U32 width = m_r->getInternalResolution().x() / 2;
 	const U32 height = m_r->getInternalResolution().y() / 2;
+	const Bool preferCompute = getConfig().getRPreferCompute();
 
 	ANKI_R_LOGV("Initializing indirect specular. Resolution %ux%u", width, height);
 
 	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoise_Rgba8_64x64.png", m_noiseImage));
 
 	// Create RT
-	m_rtDescr = m_r->create2DRenderTargetDescription(width, height, m_r->getHdrFormat(), "SSR");
-	m_rtDescr.bake();
+	TextureUsageBit usage = TextureUsageBit::ALL_SAMPLED;
+
+	usage |= (preferCompute) ? TextureUsageBit::IMAGE_COMPUTE_WRITE : TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
+
+	TextureInitInfo texInit = m_r->create2DRenderTargetInitInfo(width, height, m_r->getHdrFormat(), usage, "SSR #1");
+	m_rts[0] = m_r->createAndClearRenderTarget(texInit, TextureUsageBit::ALL_SAMPLED);
+	texInit.setName("SSR #2");
+	m_rts[1] = m_r->createAndClearRenderTarget(texInit, TextureUsageBit::ALL_SAMPLED);
 
 	m_fbDescr.m_colorAttachmentCount = 1;
 	m_fbDescr.bake();
@@ -65,8 +73,20 @@ void IndirectSpecular::populateRenderGraph(RenderingContext& ctx)
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	const Bool preferCompute = getConfig().getRPreferCompute();
 
-	// Create RTs
-	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+	// Create/import RTs
+	const U32 readRtIdx = m_r->getFrameCount() & 1;
+	const U32 writeRtIdx = !readRtIdx;
+	if(ANKI_LIKELY(m_rtsImportedOnce))
+	{
+		m_runCtx.m_rts[0] = rgraph.importRenderTarget(m_rts[readRtIdx]);
+		m_runCtx.m_rts[1] = rgraph.importRenderTarget(m_rts[writeRtIdx]);
+	}
+	else
+	{
+		m_runCtx.m_rts[0] = rgraph.importRenderTarget(m_rts[readRtIdx], TextureUsageBit::ALL_SAMPLED);
+		m_runCtx.m_rts[1] = rgraph.importRenderTarget(m_rts[writeRtIdx], TextureUsageBit::ALL_SAMPLED);
+		m_rtsImportedOnce = true;
+	}
 
 	// Create pass
 	RenderPassDescriptionBase* ppass;
@@ -83,14 +103,15 @@ void IndirectSpecular::populateRenderGraph(RenderingContext& ctx)
 	else
 	{
 		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SSR");
-		pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_rt});
+		pass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_rts[WRITE]});
 
 		ppass = &pass;
 		readUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 		writeUsage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
 	}
 
-	ppass->newDependency(RenderPassDependency(m_runCtx.m_rt, writeUsage));
+	ppass->newDependency(RenderPassDependency(m_runCtx.m_rts[WRITE], writeUsage));
+	ppass->newDependency(RenderPassDependency(m_runCtx.m_rts[READ], readUsage));
 	ppass->newDependency(RenderPassDependency(m_r->getGBuffer().getColorRt(1), readUsage));
 	ppass->newDependency(RenderPassDependency(m_r->getGBuffer().getColorRt(2), readUsage));
 
@@ -100,6 +121,9 @@ void IndirectSpecular::populateRenderGraph(RenderingContext& ctx)
 
 	ppass->newDependency(RenderPassDependency(m_r->getProbeReflections().getReflectionRt(), readUsage));
 
+	ppass->newDependency(RenderPassDependency(m_r->getMotionVectors().getMotionVectorsRt(), readUsage));
+	ppass->newDependency(RenderPassDependency(m_r->getMotionVectors().getHistoryLengthRt(), readUsage));
+
 	ppass->setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
 		run(ctx, rgraphCtx);
 	});
@@ -140,18 +164,22 @@ void IndirectSpecular::run(const RenderingContext& ctx, RenderPassWorkContext& r
 
 	rgraphCtx.bindColorTexture(0, 5, m_r->getDownscaleBlur().getRt());
 
-	cmdb->bindSampler(0, 6, m_r->getSamplers().m_trilinearRepeat);
-	cmdb->bindTexture(0, 7, m_noiseImage->getTextureView());
+	rgraphCtx.bindColorTexture(0, 6, m_runCtx.m_rts[READ]);
+	rgraphCtx.bindColorTexture(0, 7, m_r->getMotionVectors().getMotionVectorsRt());
+	rgraphCtx.bindColorTexture(0, 8, m_r->getMotionVectors().getHistoryLengthRt());
+
+	cmdb->bindSampler(0, 9, m_r->getSamplers().m_trilinearRepeat);
+	cmdb->bindTexture(0, 10, m_noiseImage->getTextureView());
 
 	const ClusteredShadingContext& binning = ctx.m_clusteredShading;
-	bindUniforms(cmdb, 0, 8, binning.m_clusteredShadingUniformsToken);
-	bindUniforms(cmdb, 0, 9, binning.m_reflectionProbesToken);
-	rgraphCtx.bindColorTexture(0, 10, m_r->getProbeReflections().getReflectionRt());
-	bindStorage(cmdb, 0, 11, binning.m_clustersToken);
+	bindUniforms(cmdb, 0, 11, binning.m_clusteredShadingUniformsToken);
+	bindUniforms(cmdb, 0, 12, binning.m_reflectionProbesToken);
+	rgraphCtx.bindColorTexture(0, 13, m_r->getProbeReflections().getReflectionRt());
+	bindStorage(cmdb, 0, 14, binning.m_clustersToken);
 
 	if(getConfig().getRPreferCompute())
 	{
-		rgraphCtx.bindImage(0, 12, m_runCtx.m_rt, TextureSubresourceInfo());
+		rgraphCtx.bindImage(0, 15, m_runCtx.m_rts[WRITE], TextureSubresourceInfo());
 
 		dispatchPPCompute(cmdb, 8, 8, m_r->getInternalResolution().x() / 2, m_r->getInternalResolution().y() / 2);
 	}

+ 9 - 4
AnKi/Renderer/IndirectSpecular.h

@@ -31,14 +31,19 @@ public:
 
 	RenderTargetHandle getRt() const
 	{
-		return m_runCtx.m_rt;
+		return m_runCtx.m_rts[WRITE];
 	}
 
 private:
+	static constexpr U32 READ = 0;
+	static constexpr U32 WRITE = 1;
+
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 
-	RenderTargetDescription m_rtDescr;
+	Array<TexturePtr, 2> m_rts;
+	Bool m_rtsImportedOnce = false;
+
 	FramebufferDescription m_fbDescr;
 
 	ImageResourcePtr m_noiseImage;
@@ -46,7 +51,7 @@ private:
 	class
 	{
 	public:
-		RenderTargetHandle m_rt;
+		Array<RenderTargetHandle, 2> m_rts;
 	} m_runCtx;
 
 	ANKI_USE_RESULT Error initInternal();
@@ -57,7 +62,7 @@ private:
 							  ShaderProgramPtr& optionalShaderProgram) const override
 	{
 		ANKI_ASSERT(rtName == "SSR");
-		handle = m_runCtx.m_rt;
+		handle = m_runCtx.m_rts[WRITE];
 	}
 };
 /// @}

+ 10 - 23
AnKi/ShaderCompiler/Glslang.cpp

@@ -229,43 +229,30 @@ static ANKI_USE_RESULT Error parseErrorLine(CString error, GenericMemoryPoolAllo
 
 static ANKI_USE_RESULT Error logShaderErrorCode(CString error, CString source, GenericMemoryPoolAllocator<U8> alloc)
 {
-	U32 errorLineNumber = 0;
-	ANKI_CHECK(parseErrorLine(error, alloc, errorLineNumber));
+	U32 errorLineNumberu = 0;
+	ANKI_CHECK(parseErrorLine(error, alloc, errorLineNumberu));
+	const I32 errorLineNumber = I32(errorLineNumberu);
+
+	constexpr I32 lineCountAroundError = 4;
 
 	StringAuto prettySrc(alloc);
 	StringListAuto lines(alloc);
-	StringAuto errorLineTxt(alloc);
-
-	static const char* padding = "==============================================================================";
 
 	lines.splitString(source, '\n', true);
 
-	U32 lineno = 0;
+	I32 lineno = 0;
 	for(auto it = lines.getBegin(); it != lines.getEnd(); ++it)
 	{
 		++lineno;
-		StringAuto tmp(alloc);
 
-		if(!it->isEmpty() && lineno == errorLineNumber)
-		{
-			tmp.sprintf(">>%8u: %s\n", lineno, &(*it)[0]);
-			errorLineTxt.sprintf("%s", &(*it)[0]);
-		}
-		else if(!it->isEmpty())
+		if(lineno >= errorLineNumber - lineCountAroundError && lineno <= errorLineNumber + lineCountAroundError)
 		{
-			tmp.sprintf("  %8u: %s\n", lineno, &(*it)[0]);
+			prettySrc.append(StringAuto(alloc).sprintf("%s%s\n", (lineno == errorLineNumber) ? ">>  " : "    ",
+													   (it->isEmpty()) ? " " : (*it).cstr()));
 		}
-		else
-		{
-			tmp.sprintf("  %8u:\n", lineno);
-		}
-
-		prettySrc.append(tmp);
 	}
 
-	ANKI_SHADER_COMPILER_LOGE("Shader compilation failed:\n%s\n%s\nIn: %s\n%s\n%s\n%s\n%s\nIn: %s\n", padding,
-							  &error[0], errorLineTxt.cstr(), padding, &prettySrc[0], padding, &error[0],
-							  errorLineTxt.cstr());
+	ANKI_SHADER_COMPILER_LOGE("Shader compilation failed:\n%sIn:\n%s\n", error.cstr(), prettySrc.cstr());
 
 	return Error::NONE;
 }

+ 49 - 23
AnKi/Shaders/IndirectSpecular.glsl

@@ -23,21 +23,25 @@ layout(set = 0, binding = 3) uniform ANKI_RP texture2D u_gbufferRt2;
 layout(set = 0, binding = 4) uniform texture2D u_depthRt;
 layout(set = 0, binding = 5) uniform ANKI_RP texture2D u_lightBufferRt;
 
-layout(set = 0, binding = 6) uniform sampler u_trilinearRepeatSampler;
-layout(set = 0, binding = 7) uniform ANKI_RP texture2D u_noiseTex;
+layout(set = 0, binding = 6) uniform ANKI_RP texture2D u_historyTex;
+layout(set = 0, binding = 7) uniform texture2D u_motionVectorsTex;
+layout(set = 0, binding = 8) uniform ANKI_RP texture2D u_historyLengthTex;
+
+layout(set = 0, binding = 9) uniform sampler u_trilinearRepeatSampler;
+layout(set = 0, binding = 10) uniform ANKI_RP texture2D u_noiseTex;
 const Vec2 NOISE_TEX_SIZE = Vec2(64.0);
 
 #define CLUSTERED_SHADING_SET 0u
-#define CLUSTERED_SHADING_UNIFORMS_BINDING 8u
-#define CLUSTERED_SHADING_REFLECTIONS_BINDING 9u
-#define CLUSTERED_SHADING_CLUSTERS_BINDING 11u
+#define CLUSTERED_SHADING_UNIFORMS_BINDING 11u
+#define CLUSTERED_SHADING_REFLECTIONS_BINDING 12u
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 14u
 #include <AnKi/Shaders/ClusteredShadingCommon.glsl>
 
 #if defined(ANKI_COMPUTE_SHADER)
 const UVec2 WORKGROUP_SIZE = UVec2(8, 8);
 layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
 
-layout(set = 0, binding = 12) uniform writeonly image2D u_outImg;
+layout(set = 0, binding = 15) uniform writeonly image2D u_outImg;
 #else
 layout(location = 0) in Vec2 in_uv;
 layout(location = 0) out Vec3 out_color;
@@ -81,31 +85,31 @@ void main()
 #endif
 
 	// Is rough enough to deserve SSR?
-	const F32 ssrFactor = saturate(1.0f - pow(roughness / u_unis.m_roughnessCutoff, 16.0f));
+	F32 ssrAttenuation = saturate(1.0f - pow(roughness / u_unis.m_roughnessCutoff, 16.0f));
 
 	// Do the heavy work
 	Vec3 hitPoint;
-	F32 hitAttenuation;
-	if(ssrFactor > EPSILON)
+	if(ssrAttenuation > EPSILON)
 	{
 		const U32 lod = 8u; // Use the max LOD for ray marching
 		const U32 step = u_unis.m_firstStepPixels;
 		const F32 stepf = F32(step);
 		const F32 minStepf = stepf / 4.0;
+		F32 hitAttenuation;
 		raymarchGroundTruth(viewPos, reflDir, uv, depth, u_unis.m_projMat, u_unis.m_maxSteps, u_depthRt,
 							u_trilinearClampSampler, F32(lod), u_unis.m_depthBufferSize, step,
 							U32((stepf - minStepf) * noise.x + minStepf), hitPoint, hitAttenuation);
 
-		hitAttenuation *= ssrFactor;
+		ssrAttenuation *= hitAttenuation;
 	}
 	else
 	{
-		hitAttenuation = 0.0f;
+		ssrAttenuation = 0.0f;
 	}
 
 #if EXTRA_REJECTION
 	// Reject backfacing
-	ANKI_BRANCH if(hitAttenuation > 0.0)
+	ANKI_BRANCH if(ssrAttenuation > 0.0)
 	{
 		const Vec3 hitNormal =
 			u_unis.m_normalMat
@@ -113,11 +117,11 @@ void main()
 		F32 backFaceAttenuation;
 		rejectBackFaces(reflDir, hitNormal, backFaceAttenuation);
 
-		hitAttenuation *= backFaceAttenuation;
+		ssrAttenuation *= backFaceAttenuation;
 	}
 
 	// Reject far from hit point
-	ANKI_BRANCH if(hitAttenuation > 0.0)
+	ANKI_BRANCH if(ssrAttenuation > 0.0)
 	{
 		const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
 		Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
@@ -129,13 +133,13 @@ void main()
 		const F32 rejectionMeters = 1.0;
 		const F32 diff = abs(actualZ - hitZ);
 		const F32 distAttenuation = (diff < rejectionMeters) ? 1.0 : 0.0;
-		hitAttenuation *= distAttenuation;
+		ssrAttenuation *= distAttenuation;
 	}
 #endif
 
 	// Read the reflection
-	Vec3 ssrColor = Vec3(0.0);
-	ANKI_BRANCH if(hitAttenuation > 0.0)
+	Vec3 outColor = Vec3(0.0);
+	ANKI_BRANCH if(ssrAttenuation > 0.0)
 	{
 		// Reproject the UV because you are reading the previous frame
 		const Vec4 v4 = u_unis.m_prevViewProjMatMulInvViewProjMat * Vec4(UV_TO_NDC(hitPoint.xy), hitPoint.z, 1.0);
@@ -150,13 +154,33 @@ void main()
 #endif
 
 		// Read the light buffer
-		ssrColor = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, lod).rgb;
+		Vec3 ssrColor = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, lod).rgb;
 		ssrColor = clamp(ssrColor, 0.0, MAX_F32); // Fix the value just in case
+
+		outColor = ssrColor;
+	}
+
+	// Blend with history
+	{
+		const Vec2 historyUv = uv + textureLod(u_motionVectorsTex, u_trilinearClampSampler, uv, 0.0).xy;
+		const F32 historyLength = textureLod(u_historyLengthTex, u_trilinearClampSampler, uv, 0.0).x;
+
+		const F32 lowestBlendFactor = 0.2;
+		const F32 maxHistoryLength = 16.0;
+		const F32 stableFrames = 4.0;
+		const F32 lerp = min(1.0, (historyLength * maxHistoryLength - 1.0) / stableFrames);
+		const F32 blendFactor = mix(1.0, lowestBlendFactor, lerp);
+
+		// Blend with history
+		if(blendFactor < 1.0)
+		{
+			const ANKI_RP Vec3 history = textureLod(u_historyTex, u_trilinearClampSampler, historyUv, 0.0).xyz;
+			outColor = mix(history, outColor, blendFactor);
+		}
 	}
 
 	// Read probes
-	Vec3 probeColor = Vec3(0.0);
-	ANKI_BRANCH if(hitAttenuation < 1.0)
+	ANKI_BRANCH if(ssrAttenuation < 1.0)
 	{
 #if defined(ANKI_COMPUTE_SHADER)
 		const Vec2 fragCoord = Vec2(gl_GlobalInvocationID.xy) + 0.5;
@@ -184,6 +208,8 @@ void main()
 		const Vec3 reflDir = reflect(-viewDir, worldNormal);
 #endif
 
+		Vec3 probeColor = Vec3(0.0);
+
 		if(bitCount(cluster.m_reflectionProbesMask) == 1)
 		{
 			// Only one probe, do a fast path without blend weight
@@ -223,12 +249,12 @@ void main()
 			// Normalize the colors
 			probeColor /= totalBlendWeight;
 		}
-	}
 
-	// Compute final value
-	const Vec3 outColor = mix(probeColor, ssrColor, hitAttenuation);
+		outColor = mix(probeColor, outColor, ssrAttenuation);
+	}
 
 	// Store
+	ssrAttenuation = saturate(ssrAttenuation);
 #if defined(ANKI_COMPUTE_SHADER)
 	imageStore(u_outImg, IVec2(gl_GlobalInvocationID.xy), Vec4(outColor, 0.0));
 #else