소스 검색

Add some SVGF ideas back to the bilateral denoiser

Panagiotis Christopoulos Charitos 4 년 전
부모
커밋
e24357899f

+ 2 - 2
AnKi/Renderer/ConfigDefs.h

@@ -50,5 +50,5 @@ ANKI_CONFIG_OPTION(r_bloomScale, 2.5, 0.0, 256.0)
 
 ANKI_CONFIG_OPTION(r_smResolveFactor, 0.5, 0.25, 1.0)
 
-ANKI_CONFIG_OPTION(r_rtShadowsSvgf, 0, 1, 0)
-ANKI_CONFIG_OPTION(r_rtShadowsSvgfAtrousPassCount, 1, 5, 1)
+ANKI_CONFIG_OPTION(r_rtShadowsSvgf, 0, 0, 1)
+ANKI_CONFIG_OPTION(r_rtShadowsSvgfAtrousPassCount, 1, 1, 20)

+ 47 - 56
AnKi/Renderer/RtShadows.cpp

@@ -38,18 +38,21 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 	m_atrousPassCount = cfg.getNumberU8("r_rtShadowsSvgfAtrousPassCount");
 
 	// Ray gen program
-	ANKI_CHECK(getResourceManager().loadResource("Shaders/RtShadowsRayGen.ankiprog", m_rayGenProg));
-	ShaderProgramResourceVariantInitInfo variantInitInfo(m_rayGenProg);
-	variantInitInfo.addMutation("SVGF", m_useSvgf);
-	const ShaderProgramResourceVariant* variant;
-	m_rayGenProg->getOrCreateVariant(variantInitInfo, variant);
-	m_rtLibraryGrProg = variant->getProgram();
-	m_rayGenShaderGroupIdx = variant->getShaderGroupHandleIndex();
+	{
+		ANKI_CHECK(getResourceManager().loadResource("Shaders/RtShadowsRayGen.ankiprog", m_rayGenProg));
+		const ShaderProgramResourceVariant* variant;
+		m_rayGenProg->getOrCreateVariant(variant);
+		m_rtLibraryGrProg = variant->getProgram();
+		m_rayGenShaderGroupIdx = variant->getShaderGroupHandleIndex();
+	}
 
 	// Miss prog
-	ANKI_CHECK(getResourceManager().loadResource("Shaders/RtShadowsMiss.ankiprog", m_missProg));
-	m_missProg->getOrCreateVariant(variant);
-	m_missShaderGroupIdx = variant->getShaderGroupHandleIndex();
+	{
+		ANKI_CHECK(getResourceManager().loadResource("Shaders/RtShadowsMiss.ankiprog", m_missProg));
+		const ShaderProgramResourceVariant* variant;
+		m_missProg->getOrCreateVariant(variant);
+		m_missShaderGroupIdx = variant->getShaderGroupHandleIndex();
+	}
 
 	// Denoise program
 	if(!m_useSvgf)
@@ -57,10 +60,13 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 		ANKI_CHECK(getResourceManager().loadResource("Shaders/RtShadowsDenoise.ankiprog", m_denoiseProg));
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_denoiseProg);
 		variantInitInfo.addConstant("OUT_IMAGE_SIZE", UVec2(m_r->getWidth(), m_r->getHeight()));
-		variantInitInfo.addConstant("SAMPLE_COUNT", 8u);
-		variantInitInfo.addConstant("SPIRAL_TURN_COUNT", 27u);
-		variantInitInfo.addConstant("PIXEL_RADIUS", 12u);
+		variantInitInfo.addConstant("SPIRAL_TURN_COUNT", 28u);
+		variantInitInfo.addConstant("MIN_SAMPLE_COUNT", 4u);
+		variantInitInfo.addConstant("MAX_SAMPLE_COUNT", 32u);
+		variantInitInfo.addConstant("MIN_PIXEL_RADIUS", 8u);
+		variantInitInfo.addConstant("MAX_PIXEL_RADIUS", 16u);
 
+		const ShaderProgramResourceVariant* variant;
 		m_denoiseProg->getOrCreateVariant(variantInitInfo, variant);
 		m_grDenoiseProg = variant->getProgram();
 	}
@@ -72,6 +78,7 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 		ShaderProgramResourceVariantInitInfo variantInitInfo(m_svgfVarianceProg);
 		variantInitInfo.addConstant("FB_SIZE", UVec2(m_r->getWidth() / 2, m_r->getHeight() / 2));
 
+		const ShaderProgramResourceVariant* variant;
 		m_svgfVarianceProg->getOrCreateVariant(variantInitInfo, variant);
 		m_svgfVarianceGrProg = variant->getProgram();
 	}
@@ -84,6 +91,7 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 		variantInitInfo.addConstant("FB_SIZE", UVec2(m_r->getWidth() / 2, m_r->getHeight() / 2));
 		variantInitInfo.addMutation("LAST_PASS", 0);
 
+		const ShaderProgramResourceVariant* variant;
 		m_svgfAtrousProg->getOrCreateVariant(variantInitInfo, variant);
 		m_svgfAtrousGrProg = variant->getProgram();
 
@@ -116,7 +124,6 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 	}
 
 	// Moments RT
-	if(m_useSvgf)
 	{
 		TextureInitInfo texinit =
 			m_r->create2DRenderTargetInitInfo(m_r->getWidth() / 2, m_r->getHeight() / 2, Format::R32G32_SFLOAT,
@@ -131,7 +138,6 @@ Error RtShadows::initInternal(const ConfigSet& cfg)
 	}
 
 	// History len RT
-	if(m_useSvgf)
 	{
 		TextureInitInfo texinit =
 			m_r->create2DRenderTargetInitInfo(m_r->getWidth() / 2, m_r->getHeight() / 2, Format::R8_UNORM,
@@ -176,38 +182,31 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 			m_runCtx.m_historyAndFinalRt =
 				rgraph.importRenderTarget(m_historyAndFinalRt, TextureUsageBit::SAMPLED_FRAGMENT);
 
-			if(m_useSvgf)
-			{
-				m_runCtx.m_prevMomentsRt =
-					rgraph.importRenderTarget(m_momentsRts[prevRtIdx], TextureUsageBit::SAMPLED_FRAGMENT);
+			m_runCtx.m_prevMomentsRt =
+				rgraph.importRenderTarget(m_momentsRts[prevRtIdx], TextureUsageBit::SAMPLED_FRAGMENT);
 
-				m_runCtx.m_prevHistoryLengthRt =
-					rgraph.importRenderTarget(m_historyLengthRts[prevRtIdx], TextureUsageBit::SAMPLED_FRAGMENT);
-			}
+			m_runCtx.m_prevHistoryLengthRt =
+				rgraph.importRenderTarget(m_historyLengthRts[prevRtIdx], TextureUsageBit::SAMPLED_FRAGMENT);
 
 			m_rtsImportedOnce = true;
 		}
 		else
 		{
 			m_runCtx.m_historyAndFinalRt = rgraph.importRenderTarget(m_historyAndFinalRt);
-
-			if(m_useSvgf)
-			{
-				m_runCtx.m_prevMomentsRt = rgraph.importRenderTarget(m_momentsRts[prevRtIdx]);
-				m_runCtx.m_prevHistoryLengthRt = rgraph.importRenderTarget(m_historyLengthRts[prevRtIdx]);
-			}
+			m_runCtx.m_prevMomentsRt = rgraph.importRenderTarget(m_momentsRts[prevRtIdx]);
+			m_runCtx.m_prevHistoryLengthRt = rgraph.importRenderTarget(m_historyLengthRts[prevRtIdx]);
 		}
 
 		m_runCtx.m_intermediateShadowsRts[0] = rgraph.newRenderTarget(m_intermediateShadowsRtDescr);
 
+		m_runCtx.m_currentMomentsRt = rgraph.importRenderTarget(m_momentsRts[!prevRtIdx], TextureUsageBit::NONE);
+		m_runCtx.m_currentHistoryLengthRt =
+			rgraph.importRenderTarget(m_historyLengthRts[!prevRtIdx], TextureUsageBit::NONE);
+
 		if(m_useSvgf)
 		{
 			m_runCtx.m_intermediateShadowsRts[1] = rgraph.newRenderTarget(m_intermediateShadowsRtDescr);
 
-			m_runCtx.m_currentMomentsRt = rgraph.importRenderTarget(m_momentsRts[!prevRtIdx], TextureUsageBit::NONE);
-			m_runCtx.m_currentHistoryLengthRt =
-				rgraph.importRenderTarget(m_historyLengthRts[!prevRtIdx], TextureUsageBit::NONE);
-
 			if(m_atrousPassCount > 1)
 			{
 				m_runCtx.m_varianceRts[0] = rgraph.newRenderTarget(m_varianceRtDescr);
@@ -236,12 +235,11 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 			RenderPassDependency(m_r->getMotionVectors().getRejectionFactorRt(), TextureUsageBit::SAMPLED_TRACE_RAYS));
 		rpass.newDependency(RenderPassDependency(m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_TRACE_RAYS));
 
+		rpass.newDependency(RenderPassDependency(m_runCtx.m_prevMomentsRt, TextureUsageBit::SAMPLED_TRACE_RAYS));
+		rpass.newDependency(RenderPassDependency(m_runCtx.m_currentMomentsRt, TextureUsageBit::IMAGE_TRACE_RAYS_WRITE));
+
 		if(m_useSvgf)
 		{
-			rpass.newDependency(RenderPassDependency(m_runCtx.m_prevMomentsRt, TextureUsageBit::SAMPLED_TRACE_RAYS));
-			rpass.newDependency(
-				RenderPassDependency(m_runCtx.m_currentMomentsRt, TextureUsageBit::IMAGE_TRACE_RAYS_WRITE));
-
 			rpass.newDependency(
 				RenderPassDependency(m_runCtx.m_prevHistoryLengthRt, TextureUsageBit::SAMPLED_TRACE_RAYS));
 			rpass.newDependency(
@@ -263,6 +261,8 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 			RenderPassDependency(m_runCtx.m_intermediateShadowsRts[0], TextureUsageBit::SAMPLED_COMPUTE));
 		rpass.newDependency(RenderPassDependency(m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE));
 		rpass.newDependency(RenderPassDependency(m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE));
+		rpass.newDependency(RenderPassDependency(m_runCtx.m_currentMomentsRt, TextureUsageBit::SAMPLED_COMPUTE));
+		rpass.newDependency(RenderPassDependency(m_runCtx.m_currentHistoryLengthRt, TextureUsageBit::SAMPLED_COMPUTE));
 
 		rpass.newDependency(RenderPassDependency(m_runCtx.m_historyAndFinalRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
 	}
@@ -426,22 +426,10 @@ void RtShadows::run(RenderPassWorkContext& rgraphCtx)
 	rgraphCtx.bindColorTexture(0, 13, m_r->getMotionVectors().getRejectionFactorRt());
 	rgraphCtx.bindColorTexture(0, 14, m_r->getGBuffer().getColorRt(2));
 	rgraphCtx.bindAccelerationStructure(0, 15, m_r->getAccelerationStructureBuilder().getAccelerationStructureHandle());
-
-	if(!m_useSvgf)
-	{
-		// Bind something random
-		rgraphCtx.bindColorTexture(0, 16, m_r->getMotionVectors().getMotionVectorsRt());
-		rgraphCtx.bindImage(0, 17, m_runCtx.m_intermediateShadowsRts[0]);
-		rgraphCtx.bindColorTexture(0, 18, m_r->getMotionVectors().getMotionVectorsRt());
-		rgraphCtx.bindImage(0, 19, m_runCtx.m_intermediateShadowsRts[0]);
-	}
-	else
-	{
-		rgraphCtx.bindColorTexture(0, 16, m_runCtx.m_prevHistoryLengthRt);
-		rgraphCtx.bindImage(0, 17, m_runCtx.m_currentHistoryLengthRt);
-		rgraphCtx.bindColorTexture(0, 18, m_runCtx.m_prevMomentsRt);
-		rgraphCtx.bindImage(0, 19, m_runCtx.m_currentMomentsRt);
-	}
+	rgraphCtx.bindColorTexture(0, 16, m_runCtx.m_prevHistoryLengthRt);
+	rgraphCtx.bindImage(0, 17, m_runCtx.m_currentHistoryLengthRt);
+	rgraphCtx.bindColorTexture(0, 18, m_runCtx.m_prevMomentsRt);
+	rgraphCtx.bindImage(0, 19, m_runCtx.m_currentMomentsRt);
 
 	cmdb->bindAllBindless(1);
 
@@ -463,11 +451,14 @@ void RtShadows::runDenoise(RenderPassWorkContext& rgraphCtx)
 	cmdb->bindShaderProgram(m_grDenoiseProg);
 
 	cmdb->bindSampler(0, 0, m_r->getSamplers().m_nearestNearestClamp);
-	rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_intermediateShadowsRts[0]);
-	rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
-	rgraphCtx.bindColorTexture(0, 3, m_r->getGBuffer().getColorRt(2));
+	cmdb->bindSampler(0, 1, m_r->getSamplers().m_trilinearClamp);
+	rgraphCtx.bindColorTexture(0, 2, m_runCtx.m_intermediateShadowsRts[0]);
+	rgraphCtx.bindTexture(0, 3, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
+	rgraphCtx.bindColorTexture(0, 4, m_r->getGBuffer().getColorRt(2));
+	rgraphCtx.bindColorTexture(0, 5, m_runCtx.m_currentMomentsRt);
+	rgraphCtx.bindColorTexture(0, 6, m_runCtx.m_currentHistoryLengthRt);
 
-	rgraphCtx.bindImage(0, 4, m_runCtx.m_historyAndFinalRt);
+	rgraphCtx.bindImage(0, 7, m_runCtx.m_historyAndFinalRt);
 
 	RtShadowsDenoiseUniforms unis;
 	unis.invViewProjMat = m_runCtx.m_ctx->m_matrices.m_invertedViewProjectionJitter;

+ 92 - 17
AnKi/Shaders/RtShadowsDenoise.ankiprog

@@ -6,9 +6,11 @@
 #pragma anki start comp
 
 ANKI_SPECIALIZATION_CONSTANT_UVEC2(OUT_IMAGE_SIZE, 0, UVec2(1));
-ANKI_SPECIALIZATION_CONSTANT_U32(SAMPLE_COUNT, 2, 1);
-ANKI_SPECIALIZATION_CONSTANT_U32(PIXEL_RADIUS, 3, 1);
-ANKI_SPECIALIZATION_CONSTANT_U32(SPIRAL_TURN_COUNT, 4, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(MIN_SAMPLE_COUNT, 2, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(MAX_SAMPLE_COUNT, 3, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(MIN_PIXEL_RADIUS, 4, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(MAX_PIXEL_RADIUS, 5, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(SPIRAL_TURN_COUNT, 6, 1);
 
 #include <AnKi/Shaders/BilateralFilter.glsl>
 #include <AnKi/Shaders/Pack.glsl>
@@ -18,10 +20,13 @@ const UVec2 WORKGROUP_SIZE = UVec2(8u, 8u);
 layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
 
 layout(set = 0, binding = 0) uniform sampler u_nearestAnyClampSampler;
-layout(set = 0, binding = 1) uniform utexture2D u_inTex;
-layout(set = 0, binding = 2) uniform texture2D u_depthTex;
-layout(set = 0, binding = 3) uniform texture2D u_gbuffer2Tex;
-layout(set = 0, binding = 4) writeonly uniform uimage2D u_outImg;
+layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
+layout(set = 0, binding = 2) uniform utexture2D u_inTex;
+layout(set = 0, binding = 3) uniform texture2D u_depthTex;
+layout(set = 0, binding = 4) uniform texture2D u_gbuffer2Tex;
+layout(set = 0, binding = 5) uniform texture2D u_momentsTex;
+layout(set = 0, binding = 6) uniform texture2D u_historyLengthTex;
+layout(set = 0, binding = 7) writeonly uniform uimage2D u_outImg;
 
 layout(std430, push_constant, row_major) uniform b_pc
 {
@@ -35,6 +40,36 @@ Vec3 unproject(Vec2 ndc, F32 depth)
 	return worldPos;
 }
 
+F32 computeVarianceCenter(Vec2 uv)
+{
+	const F32 kernel[2][2] = {{1.0 / 4.0, 1.0 / 8.0}, {1.0 / 8.0, 1.0 / 16.0}};
+	const I32 radius = 1;
+	const Vec2 texelSize = 1.0 / Vec2(textureSize(u_momentsTex, 0).xy);
+	Vec2 sumMoments = Vec2(0.0);
+
+	for(I32 yy = -radius; yy <= radius; yy++)
+	{
+		for(I32 xx = -radius; xx <= radius; xx++)
+		{
+			const Vec2 newUv = uv + Vec2(xx, yy) * texelSize;
+			const F32 k = kernel[abs(xx)][abs(yy)];
+			sumMoments += textureLod(u_momentsTex, u_linearAnyClampSampler, newUv, 0.0).xy * k;
+		}
+	}
+
+	return abs(sumMoments.y - sumMoments.x * sumMoments.x);
+}
+
+F32 computeShadowsLuma(F32 shadowLayers[MAX_RT_SHADOW_LAYERS])
+{
+	F32 l = 0.0;
+	ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
+	{
+		l += shadowLayers[i];
+	}
+	return min(1.0, l / 2.0);
+}
+
 void main()
 {
 	// Set UVs
@@ -47,37 +82,77 @@ void main()
 	const Vec2 uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(OUT_IMAGE_SIZE);
 
 	// Reference
+	const F32 depthCenter = textureLod(u_depthTex, u_linearAnyClampSampler, uv, 0.0).r;
+	if(depthCenter == 1.0)
+	{
+		imageStore(u_outImg, IVec2(gl_GlobalInvocationID.xy), UVec4(0));
+		return;
+	}
+
+	const Vec3 positionCenter = unproject(UV_TO_NDC(uv), depthCenter);
+	const Vec3 normalCenter = readNormalFromGBuffer(u_gbuffer2Tex, u_linearAnyClampSampler, uv);
+
 	F32 shadowFactors[MAX_RT_SHADOW_LAYERS];
 	unpackRtShadows(textureLod(u_inTex, u_nearestAnyClampSampler, uv, 0.0), shadowFactors);
-	F32 weight = 1.0;
 
-	const F32 depthCenter = textureLod(u_depthTex, u_nearestAnyClampSampler, uv, 0.0).r;
-	const Vec3 positionCenter = unproject(UV_TO_NDC(uv), depthCenter);
-	const Vec3 normalCenter = readNormalFromGBuffer(u_gbuffer2Tex, u_nearestAnyClampSampler, uv);
+	// Decide the amount of blurring
+	const F32 varianceCenter = computeVarianceCenter(uv);
+	const F32 historyLength =
+		textureLod(u_historyLengthTex, u_linearAnyClampSampler, uv, 0.0).x * RT_SHADOWS_MAX_HISTORY_LENGTH;
+
+	U32 pixelRadius;
+	U32 sampleCount;
+	if(historyLength < 2.0)
+	{
+		// Worst case
+		pixelRadius = MAX_PIXEL_RADIUS;
+		sampleCount = MAX_SAMPLE_COUNT;
+	}
+	else if(historyLength > 4.0 && varianceCenter < 0.0001)
+	{
+		// Best case
+		pixelRadius = MIN_PIXEL_RADIUS;
+		sampleCount = MIN_SAMPLE_COUNT;
+	}
+	else
+	{
+		// Every other case
+
+		F32 blur = varianceCenter * 100.0;
+		blur = min(1.0, blur);
+
+		const F32 pixelRadiusf = mix(F32(MIN_PIXEL_RADIUS), F32(MAX_PIXEL_RADIUS), blur);
+		const F32 sampleCountf = mix(F32(MIN_SAMPLE_COUNT), F32(MAX_SAMPLE_COUNT), blur);
+
+		pixelRadius = U32(pixelRadiusf);
+		sampleCount = U32(sampleCountf);
+	}
 
 	// Sample
 	SpatialBilateralContext ctx =
-		spatialBilateralInit(SAMPLE_COUNT, gl_GlobalInvocationID.xy, PIXEL_RADIUS, SPIRAL_TURN_COUNT, u_unis.time);
+		spatialBilateralInit(sampleCount, gl_GlobalInvocationID.xy, pixelRadius, SPIRAL_TURN_COUNT, u_unis.time);
+	F32 weight = 1.0;
 
-	for(U32 i = 0; i < SAMPLE_COUNT; ++i)
+	for(U32 i = 0; i < sampleCount; ++i)
 	{
 		const IVec2 unormalizedUvs = clamp(IVec2(spatialBilateralIterate(ctx, i)), IVec2(0), IVec2(OUT_IMAGE_SIZE - 1));
 		const Vec2 sampleUv = Vec2(unormalizedUvs) / Vec2(OUT_IMAGE_SIZE);
 
+		F32 localShadowFactors[MAX_RT_SHADOW_LAYERS];
+		unpackRtShadows(texelFetch(u_inTex, IVec2(unormalizedUvs / 2), 0), localShadowFactors);
+
 		const F32 depthTap = texelFetch(u_depthTex, unormalizedUvs, 0).r;
 		const Vec3 positionTap = unproject(UV_TO_NDC(sampleUv), depthTap);
 		const Vec3 normalTap = unpackNormalFromGBuffer(texelFetch(u_gbuffer2Tex, unormalizedUvs, 0));
 
 		const F32 w = calculateBilateralWeightPlane(positionCenter, normalCenter, positionTap, normalTap, 1.0);
-		weight += w;
-
-		F32 localShadowFactors[MAX_RT_SHADOW_LAYERS];
-		unpackRtShadows(texelFetch(u_inTex, IVec2(unormalizedUvs / 2), 0), localShadowFactors);
 
 		ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
 		{
 			shadowFactors[i] += localShadowFactors[i] * w;
 		}
+
+		weight += w;
 	}
 
 	// Write value

+ 27 - 34
AnKi/Shaders/RtShadowsRayGen.ankiprog

@@ -3,8 +3,6 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator SVGF 0 1
-
 #pragma anki library RtShadows
 #pragma anki ray_type 0
 
@@ -30,12 +28,10 @@ layout(set = 0, binding = 12) uniform texture2D u_motionVectorsRt;
 layout(set = 0, binding = 13) uniform texture2D u_motionVectorsRejectionRt;
 layout(set = 0, binding = 14) uniform texture2D u_normalRt;
 layout(set = 0, binding = 15) uniform accelerationStructureEXT u_tlas;
-#if SVGF
 layout(set = 0, binding = 16) uniform texture2D u_prevHistoryLengthTex;
 layout(set = 0, binding = 17) uniform image2D u_historyLengthImage;
 layout(set = 0, binding = 18) uniform texture2D u_prevMomentsTex;
 layout(set = 0, binding = 19) uniform image2D u_momentsImage;
-#endif
 
 ANKI_BINDLESS_SET(1); // Used by the hit shaders
 
@@ -74,13 +70,12 @@ void main()
 	if(depth == 1.0)
 	{
 		imageStore(u_shadowsImage, IVec2(gl_LaunchIDEXT.xy), UVec4(0));
-#if SVGF
 		imageStore(u_momentsImage, IVec2(gl_LaunchIDEXT.xy), Vec4(0.0));
 
 		// Set to max history length because this pixel won't need any processing from further compute stages
 		imageStore(u_historyLengthImage, IVec2(gl_LaunchIDEXT.xy),
 				   Vec4(RT_SHADOWS_MAX_HISTORY_LENGTH / RT_SHADOWS_MAX_HISTORY_LENGTH, 0.0, 0.0, 0.0));
-#endif
+
 		return;
 	}
 
@@ -154,30 +149,47 @@ void main()
 		}
 	}
 
-	// Get history info
+	// Compute history length
 	const Vec2 historyUv = uv + textureLod(u_motionVectorsRt, u_linearAnyClampSampler, uv, 0.0).xy;
 	const F32 historyRejectionFactor = textureLod(u_motionVectorsRejectionRt, u_linearAnyClampSampler, uv, 0.0).x;
-	const F32 nominalBlendFactor = 0.1;
-	const F32 blendFactor = mix(nominalBlendFactor, 1.0, historyRejectionFactor);
+	F32 historyLength;
+	if(historyRejectionFactor >= 0.5)
+	{
+		// Rejection factor too high, reset the temporal history for all layers
+		historyLength = 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
+	}
+	else
+	{
+		// Sample seems stable, increment its temporal history
 
-	// Read the history. Use nearest sampler because it's an integer texture
+		historyLength = textureLod(u_prevHistoryLengthTex, u_linearAnyClampSampler, historyUv, 0.0).r;
+		historyLength += 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
+	}
+
+	// Store history length
+	imageStore(u_historyLengthImage, IVec2(gl_LaunchIDEXT.xy), Vec4(historyLength));
+
+	// Compute blend fractor. Use nearest sampler because it's an integer texture
+	const F32 lowestBlendFactor = 0.05;
+	const F32 stableFrames = 4.0;
+	const F32 lerp = min(1.0, (historyLength * RT_SHADOWS_MAX_HISTORY_LENGTH - 1.0) / stableFrames);
+	const F32 blendFactor = mix(1.0, lowestBlendFactor, lerp);
+
+	// Blend with history
 	const UVec4 packedhistory = textureLod(u_historyShadowsTex, u_nearestAnyClampSampler, historyUv, 0.0);
 	F32 history[MAX_RT_SHADOW_LAYERS];
 	unpackRtShadows(packedhistory, history);
-
-	// Blend with history
 	for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
 	{
 		const F32 lerp = min(1.0, u_unis.historyRejectFactor[i] + blendFactor);
 		shadowFactors[i] = mix(history[i], shadowFactors[i], lerp);
 	}
 
-	// Store the 1st image
+	// Store the shadows image
 	const UVec4 packed = packRtShadows(shadowFactors);
 	imageStore(u_shadowsImage, IVec2(gl_LaunchIDEXT.xy), packed);
 
-#if SVGF
-	// Compute the moments
+	// Compute the moments that will give temporal variance
 	Vec2 moments = Vec2(0.0);
 	ANKI_UNROLL for(U32 i = 0; i < MAX_RT_SHADOW_LAYERS; ++i)
 	{
@@ -193,24 +205,5 @@ void main()
 
 	// Store the moments
 	imageStore(u_momentsImage, IVec2(gl_LaunchIDEXT.xy), Vec4(moments, 0.0, 0.0));
-
-	// Compute the temporal history (Gives the number of frames with temporal stability)
-	F32 historyLength;
-	if(historyRejectionFactor >= 0.9)
-	{
-		// Rejection factor too high, reset the temporal history for all layers
-		historyLength = 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
-	}
-	else
-	{
-		// Sample seems stable, increment it's temporal history
-
-		historyLength = textureLod(u_prevHistoryLengthTex, u_linearAnyClampSampler, historyUv, 0.0).r;
-		historyLength += 1.0 / RT_SHADOWS_MAX_HISTORY_LENGTH;
-	}
-
-	// Store temporal history
-	imageStore(u_historyLengthImage, IVec2(gl_LaunchIDEXT.xy), Vec4(historyLength));
-#endif
 }
 #pragma anki end

+ 2 - 1
AnKi/Shaders/RtShadowsVisualizeRenderTarget.ankiprog

@@ -20,8 +20,9 @@ layout(location = 0) out Vec3 out_color;
 
 void main()
 {
+	const UVec4 packed = textureLod(u_inTex, u_nearestAnyClampSampler, in_uv, 0.0);
 	F32 shadowFactors[MAX_RT_SHADOW_LAYERS];
-	unpackRtShadows(textureLod(u_inTex, u_nearestAnyClampSampler, in_uv, 0.0), shadowFactors);
+	unpackRtShadows(packed, shadowFactors);
 
 #if LAYER_GROUP == 0
 	out_color = Vec3(shadowFactors[0], shadowFactors[1], shadowFactors[2]);