Browse Source

Convert the volumetric effects. Not working yet

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
b18c9346af

+ 1 - 1
AnKi/Renderer/ClusterBinning.cpp

@@ -423,7 +423,7 @@ void ClusterBinning::writeClustererBuffersTask()
 			(rqueue.m_cameraNear - rqueue.m_cameraFar) / (rqueue.m_cameraNear * F32(m_r->getZSplitCount()));
 			(rqueue.m_cameraNear - rqueue.m_cameraFar) / (rqueue.m_cameraNear * F32(m_r->getZSplitCount()));
 		unis.m_zSplitMagic.y() = rqueue.m_cameraFar / (rqueue.m_cameraNear * F32(m_r->getZSplitCount()));
 		unis.m_zSplitMagic.y() = rqueue.m_cameraFar / (rqueue.m_cameraNear * F32(m_r->getZSplitCount()));
 		unis.m_tileSize = m_r->getTileSize();
 		unis.m_tileSize = m_r->getTileSize();
-		unis.m_lightVolumeLastCluster = m_r->getVolumetricLightingAccumulation().getFinalClusterInZ();
+		unis.m_lightVolumeLastZSplit = m_r->getVolumetricLightingAccumulation().getFinalZSplit();
 
 
 		unis.m_pointLightCount = rqueue.m_pointLights.getSize();
 		unis.m_pointLightCount = rqueue.m_pointLights.getSize();
 		unis.m_spotLightCount = rqueue.m_spotLights.getSize();
 		unis.m_spotLightCount = rqueue.m_spotLights.getSize();

+ 3 - 3
AnKi/Renderer/ConfigDefs.h

@@ -12,9 +12,9 @@ ANKI_CONFIG_OPTION(r_zSplitCount, 64, 8, 1024, "Clusterer number of Z splits")
 
 
 ANKI_CONFIG_OPTION(r_renderingQuality, 1.0, 0.5, 1.0, "A factor over the requested renderingresolution")
 ANKI_CONFIG_OPTION(r_renderingQuality, 1.0, 0.5, 1.0, "A factor over the requested renderingresolution")
 
 
-ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationClusterFractionXY, 4, 1, 16)
-ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationClusterFractionZ, 4, 1, 16)
-ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationFinalClusterInZ, 26, 1, 256)
+ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationQualityXY, 1.0, 0.5, 16.0)
+ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationQualityZ, 1.0, 0.5, 16.0)
+ANKI_CONFIG_OPTION(r_volumetricLightingAccumulationFinalZSplit, 26, 1, 256)
 
 
 ANKI_CONFIG_OPTION(r_ssrMaxSteps, 64, 1, 2048)
 ANKI_CONFIG_OPTION(r_ssrMaxSteps, 64, 1, 2048)
 ANKI_CONFIG_OPTION(r_ssrDepthLod, 2, 0, 1000)
 ANKI_CONFIG_OPTION(r_ssrDepthLod, 2, 0, 1000)

+ 2 - 1
AnKi/Renderer/Renderer.cpp

@@ -296,6 +296,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	ctx.m_matrices.m_invertedViewProjectionJitter = ctx.m_matrices.m_viewProjectionJitter.getInverse();
 	ctx.m_matrices.m_invertedViewProjectionJitter = ctx.m_matrices.m_viewProjectionJitter.getInverse();
 	ctx.m_matrices.m_invertedViewProjection = ctx.m_matrices.m_viewProjection.getInverse();
 	ctx.m_matrices.m_invertedViewProjection = ctx.m_matrices.m_viewProjection.getInverse();
 	ctx.m_matrices.m_invertedProjectionJitter = ctx.m_matrices.m_projectionJitter.getInverse();
 	ctx.m_matrices.m_invertedProjectionJitter = ctx.m_matrices.m_projectionJitter.getInverse();
+	ctx.m_matrices.m_invertedView = ctx.m_matrices.m_view.getInverse();
 
 
 	ctx.m_matrices.m_unprojectionParameters = ctx.m_matrices.m_projection.extractPerspectiveUnprojectionParams();
 	ctx.m_matrices.m_unprojectionParameters = ctx.m_matrices.m_projection.extractPerspectiveUnprojectionParams();
 
 
@@ -608,7 +609,7 @@ void Renderer::updateLightShadingUniforms(RenderingContext& ctx) const
 	blk->m_clustererMagicValues = ctx.m_clusterBinOut.m_shaderMagicValues;
 	blk->m_clustererMagicValues = ctx.m_clusterBinOut.m_shaderMagicValues;
 	blk->m_prevClustererMagicValues = ctx.m_prevClustererMagicValues;
 	blk->m_prevClustererMagicValues = ctx.m_prevClustererMagicValues;
 
 
-	blk->m_lightVolumeLastCluster = m_volLighting->getFinalClusterInZ();
+	blk->m_lightVolumeLastCluster = m_volLighting->getFinalZSplit();
 	blk->m_frameCount = m_frameCount & MAX_U32;
 	blk->m_frameCount = m_frameCount & MAX_U32;
 
 
 	// Matrices
 	// Matrices

+ 14 - 15
AnKi/Renderer/VolumetricFog.cpp

@@ -18,16 +18,13 @@ namespace anki
 Error VolumetricFog::init(const ConfigSet& config)
 Error VolumetricFog::init(const ConfigSet& config)
 {
 {
 	// Misc
 	// Misc
-	const U32 fractionXY = config.getNumberU32("r_volumetricLightingAccumulationClusterFractionXY");
-	ANKI_ASSERT(fractionXY >= 1);
-	const U32 fractionZ = config.getNumberU32("r_volumetricLightingAccumulationClusterFractionZ");
-	ANKI_ASSERT(fractionZ >= 1);
-	m_finalClusterZ = config.getNumberU32("r_volumetricLightingAccumulationFinalClusterInZ");
-	ANKI_ASSERT(m_finalClusterZ > 0 && m_finalClusterZ < m_r->getClusterCount()[2]);
-
-	m_volumeSize[0] = m_r->getClusterCount()[0] * fractionXY;
-	m_volumeSize[1] = m_r->getClusterCount()[1] * fractionXY;
-	m_volumeSize[2] = (m_finalClusterZ + 1) * fractionZ;
+	const F32 qualityXY = config.getNumberF32("r_volumetricLightingAccumulationQualityXY");
+	const F32 qualityZ = config.getNumberF32("r_volumetricLightingAccumulationQualityZ");
+	m_finalZSplit = min(m_r->getZSplitCount(), config.getNumberU32("r_volumetricLightingAccumulationFinalZSplit"));
+
+	m_volumeSize[0] = U32(F32(m_r->getTileCounts().x()) * qualityXY);
+	m_volumeSize[1] = U32(F32(m_r->getTileCounts().y()) * qualityXY);
+	m_volumeSize[2] = U32(F32(m_finalZSplit + 1) * qualityZ);
 	ANKI_R_LOGI("Initializing volumetric fog. Size %ux%ux%u", m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]);
 	ANKI_R_LOGI("Initializing volumetric fog. Size %ux%ux%u", m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]);
 
 
 	// Shaders
 	// Shaders
@@ -35,7 +32,9 @@ Error VolumetricFog::init(const ConfigSet& config)
 
 
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
 	variantInitInfo.addConstant("VOLUME_SIZE", UVec3(m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]));
 	variantInitInfo.addConstant("VOLUME_SIZE", UVec3(m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]));
-	variantInitInfo.addConstant("FINAL_CLUSTER_Z", m_finalClusterZ);
+	variantInitInfo.addConstant("TILE_COUNT", m_r->getTileCounts());
+	variantInitInfo.addConstant("Z_SPLIT_COUNT", m_r->getZSplitCount());
+	variantInitInfo.addConstant("FINAL_Z_SPLIT", m_finalZSplit);
 
 
 	const ShaderProgramResourceVariant* variant;
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
@@ -70,17 +69,17 @@ void VolumetricFog::run(RenderPassWorkContext& rgraphCtx)
 		F32 m_fogScatteringCoeff;
 		F32 m_fogScatteringCoeff;
 		F32 m_fogAbsorptionCoeff;
 		F32 m_fogAbsorptionCoeff;
 		F32 m_density;
 		F32 m_density;
-		F32 m_padding0;
+		F32 m_near;
 		Vec3 m_fogDiffuse;
 		Vec3 m_fogDiffuse;
-		U32 m_padding1;
-		ClustererMagicValues m_clustererMagic;
+		F32 m_far;
 	} regs;
 	} regs;
 
 
 	regs.m_fogScatteringCoeff = m_fogScatteringCoeff;
 	regs.m_fogScatteringCoeff = m_fogScatteringCoeff;
 	regs.m_fogAbsorptionCoeff = m_fogAbsorptionCoeff;
 	regs.m_fogAbsorptionCoeff = m_fogAbsorptionCoeff;
 	regs.m_density = m_fogDensity;
 	regs.m_density = m_fogDensity;
 	regs.m_fogDiffuse = m_fogDiffuseColor;
 	regs.m_fogDiffuse = m_fogDiffuseColor;
-	regs.m_clustererMagic = ctx.m_clusterBinOut.m_shaderMagicValues;
+	regs.m_near = ctx.m_renderQueue->m_cameraNear;
+	regs.m_far = ctx.m_renderQueue->m_cameraFar;
 
 
 	cmdb->setPushConstants(&regs, sizeof(regs));
 	cmdb->setPushConstants(&regs, sizeof(regs));
 
 

+ 2 - 2
AnKi/Renderer/VolumetricFog.h

@@ -64,7 +64,7 @@ public:
 	/// Get the last cluster split in Z axis that will be affected by lighting.
 	/// Get the last cluster split in Z axis that will be affected by lighting.
 	U32 getFinalClusterInZ() const
 	U32 getFinalClusterInZ() const
 	{
 	{
-		return m_finalClusterZ;
+		return m_finalZSplit;
 	}
 	}
 
 
 private:
 private:
@@ -73,7 +73,7 @@ private:
 
 
 	RenderTargetDescription m_rtDescr;
 	RenderTargetDescription m_rtDescr;
 
 
-	U32 m_finalClusterZ = 0;
+	U32 m_finalZSplit = 0;
 
 
 	Array<U32, 2> m_workgroupSize = {};
 	Array<U32, 2> m_workgroupSize = {};
 	Array<U32, 3> m_volumeSize;
 	Array<U32, 3> m_volumeSize;

+ 21 - 30
AnKi/Renderer/VolumetricLightingAccumulation.cpp

@@ -25,20 +25,25 @@ VolumetricLightingAccumulation::~VolumetricLightingAccumulation()
 Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 {
 {
 	// Misc
 	// Misc
-	const U32 fractionXY = config.getNumberU32("r_volumetricLightingAccumulationClusterFractionXY");
-	ANKI_ASSERT(fractionXY >= 1);
-	const U32 fractionZ = config.getNumberU32("r_volumetricLightingAccumulationClusterFractionZ");
-	ANKI_ASSERT(fractionZ >= 1);
-	m_finalClusterZ = config.getNumberU32("r_volumetricLightingAccumulationFinalClusterInZ");
-	ANKI_ASSERT(m_finalClusterZ > 0 && m_finalClusterZ < m_r->getClusterCount()[2]);
-
-	m_volumeSize[0] = m_r->getClusterCount()[0] * fractionXY;
-	m_volumeSize[1] = m_r->getClusterCount()[1] * fractionXY;
-	m_volumeSize[2] = (m_finalClusterZ + 1) * fractionZ;
+	const F32 qualityXY = config.getNumberF32("r_volumetricLightingAccumulationQualityXY");
+	const F32 qualityZ = config.getNumberF32("r_volumetricLightingAccumulationQualityZ");
+	m_finalZSplit = min(m_r->getZSplitCount(), config.getNumberU32("r_volumetricLightingAccumulationFinalZSplit"));
+
+	m_volumeSize[0] = U32(F32(m_r->getTileCounts().x()) * qualityXY);
+	m_volumeSize[1] = U32(F32(m_r->getTileCounts().y()) * qualityXY);
+	m_volumeSize[2] = U32(F32(m_finalZSplit + 1) * qualityZ);
 	ANKI_R_LOGI("Initializing volumetric lighting accumulation. Size %ux%ux%u", m_volumeSize[0], m_volumeSize[1],
 	ANKI_R_LOGI("Initializing volumetric lighting accumulation. Size %ux%ux%u", m_volumeSize[0], m_volumeSize[1],
 				m_volumeSize[2]);
 				m_volumeSize[2]);
 
 
-	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoiseLdr16x16x16.ankitex", m_noiseTex));
+	if(!isAligned(m_r->getTileCounts().x(), m_volumeSize[0]) || !isAligned(m_r->getTileCounts().y(), m_volumeSize[1])
+	   || m_volumeSize[2] > m_r->getZSplitCount() || m_volumeSize[0] == 0 || m_volumeSize[1] == 0
+	   || m_volumeSize[2] == 0)
+	{
+		ANKI_R_LOGE("Wrong input");
+		return Error::USER_DATA;
+	}
+
+	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoiseRgb864x64.png", m_noiseTex));
 
 
 	// Shaders
 	// Shaders
 	ANKI_CHECK(getResourceManager().loadResource("Shaders/VolumetricLightingAccumulation.ankiprog", m_prog));
 	ANKI_CHECK(getResourceManager().loadResource("Shaders/VolumetricLightingAccumulation.ankiprog", m_prog));
@@ -46,12 +51,9 @@ Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
 	variantInitInfo.addMutation("ENABLE_SHADOWS", 1);
 	variantInitInfo.addMutation("ENABLE_SHADOWS", 1);
 	variantInitInfo.addConstant("VOLUME_SIZE", UVec3(m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]));
 	variantInitInfo.addConstant("VOLUME_SIZE", UVec3(m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]));
-	variantInitInfo.addConstant("CLUSTER_COUNT",
-								UVec3(m_r->getClusterCount()[0], m_r->getClusterCount()[1], m_r->getClusterCount()[2]));
-	variantInitInfo.addConstant("FINAL_CLUSTER_Z", U32(m_finalClusterZ));
-	variantInitInfo.addConstant("FRACTION", UVec3(fractionXY, fractionXY, fractionZ));
-	variantInitInfo.addConstant("NOISE_TEX_SIZE",
-								UVec3(m_noiseTex->getWidth(), m_noiseTex->getHeight(), m_noiseTex->getDepth()));
+	variantInitInfo.addConstant("TILE_COUNT", UVec2(m_r->getTileCounts().x(), m_r->getTileCounts().y()));
+	variantInitInfo.addConstant("Z_SPLIT_COUNT", m_r->getZSplitCount());
+	variantInitInfo.addConstant("FINAL_Z_SPLIT", m_finalZSplit);
 
 
 	const ShaderProgramResourceVariant* variant;
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
 	m_prog->getOrCreateVariant(variantInitInfo, variant);
@@ -101,7 +103,7 @@ void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
 {
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	RenderingContext& ctx = *m_runCtx.m_ctx;
 	RenderingContext& ctx = *m_runCtx.m_ctx;
-	const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
+	const ClustererGpuObjects& rsrc = ctx.m_clusterShading;
 
 
 	cmdb->bindShaderProgram(m_grProg);
 	cmdb->bindShaderProgram(m_grProg);
 
 
@@ -115,7 +117,7 @@ void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
 
 
 	rgraphCtx.bindColorTexture(0, 4, m_runCtx.m_rts[0]);
 	rgraphCtx.bindColorTexture(0, 4, m_runCtx.m_rts[0]);
 
 
-	bindUniforms(cmdb, 0, 5, ctx.m_lightShadingUniformsToken);
+	bindUniforms(cmdb, 0, 5, rsrc.m_clusteredShadingUniformsToken);
 	bindUniforms(cmdb, 0, 6, rsrc.m_pointLightsToken);
 	bindUniforms(cmdb, 0, 6, rsrc.m_pointLightsToken);
 	bindUniforms(cmdb, 0, 7, rsrc.m_spotLightsToken);
 	bindUniforms(cmdb, 0, 7, rsrc.m_spotLightsToken);
 	rgraphCtx.bindColorTexture(0, 8, m_r->getShadowMapping().getShadowmapRt());
 	rgraphCtx.bindColorTexture(0, 8, m_r->getShadowMapping().getShadowmapRt());
@@ -125,17 +127,6 @@ void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
 
 
 	bindUniforms(cmdb, 0, 11, rsrc.m_fogDensityVolumesToken);
 	bindUniforms(cmdb, 0, 11, rsrc.m_fogDensityVolumesToken);
 	bindStorage(cmdb, 0, 12, rsrc.m_clustersToken);
 	bindStorage(cmdb, 0, 12, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 13, rsrc.m_indicesToken);
-
-	struct PushConsts
-	{
-		Vec3 m_padding;
-		F32 m_noiseOffset;
-	} regs;
-	const F32 texelSize = 1.0f / F32(m_noiseTex->getDepth());
-	regs.m_noiseOffset = texelSize * F32(m_r->getFrameCount() % m_noiseTex->getDepth()) + texelSize / 2.0f;
-
-	cmdb->setPushConstants(&regs, sizeof(regs));
 
 
 	dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_workgroupSize[2], m_volumeSize[0],
 	dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_workgroupSize[2], m_volumeSize[0],
 					  m_volumeSize[1], m_volumeSize[2]);
 					  m_volumeSize[1], m_volumeSize[2]);

+ 3 - 3
AnKi/Renderer/VolumetricLightingAccumulation.h

@@ -31,9 +31,9 @@ public:
 	}
 	}
 
 
 	/// Get the last cluster split in Z axis that will be affected by lighting.
 	/// Get the last cluster split in Z axis that will be affected by lighting.
-	U32 getFinalClusterInZ() const
+	U32 getFinalZSplit() const
 	{
 	{
-		return m_finalClusterZ;
+		return m_finalZSplit;
 	}
 	}
 
 
 private:
 private:
@@ -43,7 +43,7 @@ private:
 	Array<TexturePtr, 2> m_rtTextures;
 	Array<TexturePtr, 2> m_rtTextures;
 	TextureResourcePtr m_noiseTex;
 	TextureResourcePtr m_noiseTex;
 
 
-	U32 m_finalClusterZ = 0;
+	U32 m_finalZSplit = 0;
 
 
 	Array<U32, 3> m_workgroupSize = {};
 	Array<U32, 3> m_workgroupSize = {};
 	Array<U32, 3> m_volumeSize;
 	Array<U32, 3> m_volumeSize;

+ 5 - 5
AnKi/Shaders/ClusteredShadingCommon2.glsl

@@ -5,7 +5,7 @@
 
 
 #pragma once
 #pragma once
 
 
-#include <AnKi/Shaders/LightFunctions.glsl>
+#include <AnKi/Shaders/LightFunctions2.glsl>
 
 
 //
 //
 // Common uniforms
 // Common uniforms
@@ -71,7 +71,7 @@ layout(set = CLUSTERED_SHADING_SET,
 #if defined(CLUSTERED_SHADING_FOG_BINDING)
 #if defined(CLUSTERED_SHADING_FOG_BINDING)
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_FOG_BINDING, scalar) uniform ANKI_RANDOM_BLOCK_NAME
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_FOG_BINDING, scalar) uniform ANKI_RANDOM_BLOCK_NAME
 {
 {
-	FogDensityVolume u_fogDensityVolumes[MAX_VISIBLE_FOG_DENSITY_VOLUMES];
+	FogDensityVolume2 u_fogDensityVolumes[MAX_VISIBLE_FOG_DENSITY_VOLUMES];
 };
 };
 #endif
 #endif
 
 
@@ -80,11 +80,11 @@ layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_FOG_BINDING, sca
 //
 //
 #if defined(CLUSTERED_SHADING_GI_BINDING)
 #if defined(CLUSTERED_SHADING_GI_BINDING)
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_GI_BINDING) uniform texture3D
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_GI_BINDING) uniform texture3D
-	u_globalIlluminationTextures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES];
+	u_globalIlluminationTextures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES2];
 
 
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_GI_BINDING + 1, scalar) uniform ANKI_RANDOM_BLOCK_NAME
 layout(set = CLUSTERED_SHADING_SET, binding = CLUSTERED_SHADING_GI_BINDING + 1, scalar) uniform ANKI_RANDOM_BLOCK_NAME
 {
 {
-	GlobalIlluminationProbe2 u_giProbes[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES];
+	GlobalIlluminationProbe2 u_giProbes[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES2];
 };
 };
 #endif
 #endif
 
 
@@ -137,7 +137,7 @@ Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask)
 
 
 	if((objectTypeMask & (1u << CLUSTER_OBJECT_TYPE_GLOBAL_ILLUMINATION_PROBE)) != 0)
 	if((objectTypeMask & (1u << CLUSTER_OBJECT_TYPE_GLOBAL_ILLUMINATION_PROBE)) != 0)
 	{
 	{
-		maxObjects += MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES;
+		maxObjects += MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES2;
 		count += bitCount(cluster.m_giProbesMask);
 		count += bitCount(cluster.m_giProbesMask);
 	}
 	}
 
 

+ 3 - 2
AnKi/Shaders/Include/ClusteredShadingTypes2.h

@@ -161,10 +161,11 @@ struct CommonMatrices
 	Mat4 m_invertedViewProjectionJitter ANKI_CPP_CODE(= Mat4::getIdentity()); ///< To unproject in world space.
 	Mat4 m_invertedViewProjectionJitter ANKI_CPP_CODE(= Mat4::getIdentity()); ///< To unproject in world space.
 	Mat4 m_invertedViewProjection ANKI_CPP_CODE(= Mat4::getIdentity());
 	Mat4 m_invertedViewProjection ANKI_CPP_CODE(= Mat4::getIdentity());
 	Mat4 m_invertedProjectionJitter ANKI_CPP_CODE(= Mat4::getIdentity()); ///< To unproject in view space.
 	Mat4 m_invertedProjectionJitter ANKI_CPP_CODE(= Mat4::getIdentity()); ///< To unproject in view space.
+	Mat4 m_invertedView ANKI_CPP_CODE(= Mat4::getIdentity());
 
 
 	Vec4 m_unprojectionParameters ANKI_CPP_CODE(= Vec4(0.0f)); ///< To unproject to view space. Jitter not considered.
 	Vec4 m_unprojectionParameters ANKI_CPP_CODE(= Vec4(0.0f)); ///< To unproject to view space. Jitter not considered.
 };
 };
-const U32 _ANKI_SIZEOF_CommonMatrices = 10u * ANKI_SIZEOF(Mat4) + ANKI_SIZEOF(Vec4);
+const U32 _ANKI_SIZEOF_CommonMatrices = 11u * ANKI_SIZEOF(Mat4) + ANKI_SIZEOF(Vec4);
 ANKI_SHADER_STATIC_ASSERT(sizeof(CommonMatrices) == _ANKI_SIZEOF_CommonMatrices);
 ANKI_SHADER_STATIC_ASSERT(sizeof(CommonMatrices) == _ANKI_SIZEOF_CommonMatrices);
 
 
 /// Common uniforms for light shading passes.
 /// Common uniforms for light shading passes.
@@ -185,7 +186,7 @@ struct ClusteredShadingUniforms
 	F32 m_zSplitCountOverFrustumLength; ///< m_zSplitCount/(far-near)
 	F32 m_zSplitCountOverFrustumLength; ///< m_zSplitCount/(far-near)
 	Vec2 m_zSplitMagic; ///< It's the "a" and "b" of computeZSplitClusterIndex(). See there for details.
 	Vec2 m_zSplitMagic; ///< It's the "a" and "b" of computeZSplitClusterIndex(). See there for details.
 	U32 m_tileSize;
 	U32 m_tileSize;
-	U32 m_lightVolumeLastCluster;
+	U32 m_lightVolumeLastZSplit;
 
 
 	U32 m_pointLightCount;
 	U32 m_pointLightCount;
 	U32 m_spotLightCount;
 	U32 m_spotLightCount;

+ 0 - 89
AnKi/Shaders/LightFunctions.glsl

@@ -11,7 +11,6 @@
 #include <AnKi/Shaders/CollisionFunctions.glsl>
 #include <AnKi/Shaders/CollisionFunctions.glsl>
 #include <AnKi/Shaders/Pack.glsl>
 #include <AnKi/Shaders/Pack.glsl>
 #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
 #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
-#include <AnKi/Shaders/Include/ClusteredShadingTypes2.h>
 #include <AnKi/Shaders/Include/ClusteredShadingFunctions.h>
 #include <AnKi/Shaders/Include/ClusteredShadingFunctions.h>
 #include <AnKi/Shaders/Include/Evsm.h>
 #include <AnKi/Shaders/Include/Evsm.h>
 
 
@@ -164,7 +163,6 @@ U32 computeShadowSampleCount(const U32 COUNT, F32 zVSpace)
 	return sampleCount;
 	return sampleCount;
 }
 }
 
 
-// TODO rm
 F32 computeShadowFactorSpotLight(SpotLight light, Vec3 worldPos, texture2D spotMap, sampler spotMapSampler)
 F32 computeShadowFactorSpotLight(SpotLight light, Vec3 worldPos, texture2D spotMap, sampler spotMapSampler)
 {
 {
 	const Vec4 texCoords4 = light.m_texProjectionMat * Vec4(worldPos, 1.0);
 	const Vec4 texCoords4 = light.m_texProjectionMat * Vec4(worldPos, 1.0);
@@ -175,18 +173,7 @@ F32 computeShadowFactorSpotLight(SpotLight light, Vec3 worldPos, texture2D spotM
 	return evsmComputeShadowFactor(texCoords3.z, shadowMoments);
 	return evsmComputeShadowFactor(texCoords3.z, shadowMoments);
 }
 }
 
 
-F32 computeShadowFactorSpotLight(SpotLight2 light, Vec3 worldPos, texture2D spotMap, sampler spotMapSampler)
-{
-	const Vec4 texCoords4 = light.m_textureMatrix * Vec4(worldPos, 1.0);
-	const Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
-
-	const Vec4 shadowMoments = textureLod(spotMap, spotMapSampler, texCoords3.xy, 0.0);
-
-	return evsmComputeShadowFactor(texCoords3.z, shadowMoments);
-}
-
 // Compute the shadow factor of point (omni) lights.
 // Compute the shadow factor of point (omni) lights.
-// TODO rm
 F32 computeShadowFactorPointLight(PointLight light, Vec3 frag2Light, texture2D shadowMap, sampler shadowMapSampler)
 F32 computeShadowFactorPointLight(PointLight light, Vec3 frag2Light, texture2D shadowMap, sampler shadowMapSampler)
 {
 {
 	const Vec3 dir = -frag2Light;
 	const Vec3 dir = -frag2Light;
@@ -229,49 +216,6 @@ F32 computeShadowFactorPointLight(PointLight light, Vec3 frag2Light, texture2D s
 	return shadowFactor;
 	return shadowFactor;
 }
 }
 
 
-// Compute the shadow factor of point (omni) lights.
-F32 computeShadowFactorPointLight(PointLight2 light, Vec3 frag2Light, texture2D shadowMap, sampler shadowMapSampler)
-{
-	const Vec3 dir = -frag2Light;
-	const Vec3 dirabs = abs(dir);
-	const F32 dist = max(dirabs.x, max(dirabs.y, dirabs.z));
-
-	// 1) Project the dist to light's proj mat
-	//
-	const F32 near = LIGHT_FRUSTUM_NEAR_PLANE;
-	const F32 far = light.m_radius;
-	const F32 g = near - far;
-
-	const F32 zVSpace = -dist;
-	const F32 w = -zVSpace;
-	F32 z = (far * zVSpace + far * near) / g;
-	z /= w;
-
-	// 2) Read shadow tex
-	//
-
-	// Convert cube coords
-	U32 faceIdxu;
-	Vec2 uv = convertCubeUvsu(dir, faceIdxu);
-
-	// Get the atlas offset
-	Vec2 atlasOffset;
-	atlasOffset.x = light.m_shadowAtlasTileOffsets[faceIdxu >> 1u][(faceIdxu & 1u) << 1u];
-	atlasOffset.y = light.m_shadowAtlasTileOffsets[faceIdxu >> 1u][((faceIdxu & 1u) << 1u) + 1u];
-
-	// Compute UV
-	uv = fma(uv, Vec2(light.m_shadowAtlasTileScale), atlasOffset);
-
-	// Sample
-	const Vec4 shadowMoments = textureLod(shadowMap, shadowMapSampler, uv, 0.0);
-
-	// 3) Compare
-	//
-	const F32 shadowFactor = evsmComputeShadowFactor(z, shadowMoments);
-
-	return shadowFactor;
-}
-
 // Compute the shadow factor of a directional light
 // Compute the shadow factor of a directional light
 F32 computeShadowFactorDirLight(DirectionalLight light, U32 cascadeIdx, Vec3 worldPos, texture2D shadowMap,
 F32 computeShadowFactorDirLight(DirectionalLight light, U32 cascadeIdx, Vec3 worldPos, texture2D shadowMap,
 								sampler shadowMapSampler)
 								sampler shadowMapSampler)
@@ -402,7 +346,6 @@ Vec3 sampleAmbientDice(Vec3 posx, Vec3 negx, Vec3 posy, Vec3 negy, Vec3 posz, Ve
 }
 }
 
 
 // Sample the irradiance term from the clipmap
 // Sample the irradiance term from the clipmap
-// TODO remove
 Vec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe probe,
 Vec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe probe,
 							  texture3D textures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES], sampler linearAnyClampSampler)
 							  texture3D textures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES], sampler linearAnyClampSampler)
 {
 {
@@ -433,35 +376,3 @@ Vec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const Glob
 
 
 	return irradiance;
 	return irradiance;
 }
 }
-
-// Sample the irradiance term from the clipmap
-Vec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe2 probe,
-							  texture3D textures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES], sampler linearAnyClampSampler)
-{
-	// Find the UVW
-	Vec3 uvw = (worldPos - probe.m_aabbMin) / (probe.m_aabbMax - probe.m_aabbMin);
-
-	// The U contains the 6 directions so divide
-	uvw.x /= 6.0;
-
-	// Calmp it to avoid direction leaking
-	uvw.x = clamp(uvw.x, probe.m_halfTexelSizeU, (1.0 / 6.0) - probe.m_halfTexelSizeU);
-
-	// Read the irradiance
-	Vec3 irradiancePerDir[6u];
-	ANKI_UNROLL for(U32 dir = 0u; dir < 6u; ++dir)
-	{
-		// Point to the correct UV
-		Vec3 shiftedUVw = uvw;
-		shiftedUVw.x += (1.0 / 6.0) * F32(dir);
-
-		irradiancePerDir[dir] =
-			textureLod(textures[nonuniformEXT(probe.m_textureIndex)], linearAnyClampSampler, shiftedUVw, 0.0).rgb;
-	}
-
-	// Sample the irradiance
-	const Vec3 irradiance = sampleAmbientDice(irradiancePerDir[0], irradiancePerDir[1], irradiancePerDir[2],
-											  irradiancePerDir[3], irradiancePerDir[4], irradiancePerDir[5], normal);
-
-	return irradiance;
-}

+ 386 - 0
AnKi/Shaders/LightFunctions2.glsl

@@ -0,0 +1,386 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// Contains functions for light calculations
+
+#pragma once
+
+#include <AnKi/Shaders/Functions.glsl>
+#include <AnKi/Shaders/CollisionFunctions.glsl>
+#include <AnKi/Shaders/Pack.glsl>
+#include <AnKi/Shaders/Include/ClusteredShadingTypes2.h>
+#include <AnKi/Shaders/Include/Evsm.h>
+
+// Do some EVSM magic with depth
+Vec2 evsmProcessDepth(F32 depth)
+{
+	depth = 2.0 * depth - 1.0;
+	const F32 pos = exp(EVSM_POSITIVE_CONSTANT * depth);
+	const F32 neg = -exp(EVSM_NEGATIVE_CONSTANT * depth);
+	return Vec2(pos, neg);
+}
+
+F32 linstep(F32 a, F32 b, F32 v)
+{
+	return saturate((v - a) / (b - a));
+}
+
+// Reduces VSM light bleedning
+F32 reduceLightBleeding(F32 pMax, F32 amount)
+{
+	// Remove the [0, amount] tail and linearly rescale (amount, 1].
+	return linstep(amount, 1.0, pMax);
+}
+
+F32 chebyshevUpperBound(Vec2 moments, F32 mean, F32 minVariance, F32 lightBleedingReduction)
+{
+	// Compute variance
+	F32 variance = moments.y - (moments.x * moments.x);
+	variance = max(variance, minVariance);
+
+	// Compute probabilistic upper bound
+	const F32 d = mean - moments.x;
+	F32 pMax = variance / (variance + (d * d));
+
+	pMax = reduceLightBleeding(pMax, lightBleedingReduction);
+
+	// One-tailed Chebyshev
+	return (mean <= moments.x) ? 1.0 : pMax;
+}
+
+// Compute the shadow factor of EVSM given the 2 depths
+F32 evsmComputeShadowFactor(F32 occluderDepth, Vec4 shadowMapMoments)
+{
+	const Vec2 evsmOccluderDepths = evsmProcessDepth(occluderDepth);
+	const Vec2 depthScale =
+		EVSM_BIAS * 0.01 * Vec2(EVSM_POSITIVE_CONSTANT, EVSM_NEGATIVE_CONSTANT) * evsmOccluderDepths;
+	const Vec2 minVariance = depthScale * depthScale;
+
+#if !ANKI_EVSM4
+	return chebyshevUpperBound(shadowMapMoments.xy, evsmOccluderDepths.x, minVariance.x, EVSM_LIGHT_BLEEDING_REDUCTION);
+#else
+	const F32 pos =
+		chebyshevUpperBound(shadowMapMoments.xy, evsmOccluderDepths.x, minVariance.x, EVSM_LIGHT_BLEEDING_REDUCTION);
+	const F32 neg =
+		chebyshevUpperBound(shadowMapMoments.zw, evsmOccluderDepths.y, minVariance.y, EVSM_LIGHT_BLEEDING_REDUCTION);
+	return min(pos, neg);
+#endif
+}
+
+// Fresnel term unreal
+// specular: The specular color aka F0
+Vec3 F_Unreal(Vec3 specular, F32 VoH)
+{
+	return specular + (1.0 - specular) * pow(2.0, (-5.55473 * VoH - 6.98316) * VoH);
+}
+
+// Fresnel Schlick: "An Inexpensive BRDF Model for Physically-Based Rendering"
+// It has lower VGRPs than F_Unreal
+// specular: The specular color aka F0
+Vec3 F_Schlick(Vec3 specular, F32 VoH)
+{
+	const F32 a = 1.0 - VoH;
+	const F32 a2 = a * a;
+	const F32 a5 = a2 * a2 * a; // a5 = a^5
+	return /*saturate(50.0 * specular.g) */ a5 + (1.0 - a5) * specular;
+}
+
+// D(n,h) aka NDF: GGX Trowbridge-Reitz
+F32 D_GGX(F32 roughness, F32 NoH)
+{
+	const F32 a = roughness * roughness;
+	const F32 a2 = a * a;
+
+	const F32 D = (NoH * a2 - NoH) * NoH + 1.0;
+	return a2 / (PI * D * D);
+}
+
+// Visibility term: Geometric shadowing divided by BRDF denominator
+F32 V_Schlick(F32 roughness, F32 NoV, F32 NoL)
+{
+	const F32 k = (roughness * roughness) * 0.5;
+	const F32 Vis_SchlickV = NoV * (1.0 - k) + k;
+	const F32 Vis_SchlickL = NoL * (1.0 - k) + k;
+	return 0.25 / (Vis_SchlickV * Vis_SchlickL);
+}
+
+Vec3 envBRDF(Vec3 specular, F32 roughness, texture2D integrationLut, sampler integrationLutSampler, F32 NoV)
+{
+	const Vec2 envBRDF = textureLod(integrationLut, integrationLutSampler, Vec2(roughness, NoV), 0.0).xy;
+	return specular * envBRDF.x + min(1.0, 50.0 * specular.g) * envBRDF.y;
+}
+
+Vec3 diffuseLambert(Vec3 diffuse)
+{
+	return diffuse * (1.0 / PI);
+}
+
+// Performs BRDF specular lighting
+Vec3 computeSpecularColorBrdf(GbufferInfo gbuffer, Vec3 viewDir, Vec3 frag2Light)
+{
+	const Vec3 H = normalize(frag2Light + viewDir);
+
+	const F32 NoL = max(EPSILON, dot(gbuffer.m_normal, frag2Light));
+	const F32 VoH = max(EPSILON, dot(viewDir, H));
+	const F32 NoH = max(EPSILON, dot(gbuffer.m_normal, H));
+	const F32 NoV = max(EPSILON, dot(gbuffer.m_normal, viewDir));
+
+	// F
+#if 0
+	const Vec3 F = F_Unreal(gbuffer.m_specular, VoH);
+#else
+	const Vec3 F = F_Schlick(gbuffer.m_specular, VoH);
+#endif
+
+	// D
+	const F32 D = D_GGX(gbuffer.m_roughness, NoH);
+
+	// Vis
+	const F32 V = V_Schlick(gbuffer.m_roughness, NoV, NoL);
+
+	return F * (V * D);
+}
+
+F32 computeSpotFactor(Vec3 l, F32 outerCos, F32 innerCos, Vec3 spotDir)
+{
+	const F32 costheta = -dot(l, spotDir);
+	const F32 spotFactor = smoothstep(outerCos, innerCos, costheta);
+	return spotFactor;
+}
+
+U32 computeShadowSampleCount(const U32 COUNT, F32 zVSpace)
+{
+	const F32 MAX_DISTANCE = 5.0;
+
+	const F32 z = max(zVSpace, -MAX_DISTANCE);
+	F32 sampleCountf = F32(COUNT) + z * (F32(COUNT) / MAX_DISTANCE);
+	sampleCountf = max(sampleCountf, 1.0);
+	const U32 sampleCount = U32(sampleCountf);
+
+	return sampleCount;
+}
+
+F32 computeShadowFactorSpotLight(SpotLight2 light, Vec3 worldPos, texture2D spotMap, sampler spotMapSampler)
+{
+	const Vec4 texCoords4 = light.m_textureMatrix * Vec4(worldPos, 1.0);
+	const Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+
+	const Vec4 shadowMoments = textureLod(spotMap, spotMapSampler, texCoords3.xy, 0.0);
+
+	return evsmComputeShadowFactor(texCoords3.z, shadowMoments);
+}
+
+// Compute the shadow factor of point (omni) lights.
+F32 computeShadowFactorPointLight(PointLight2 light, Vec3 frag2Light, texture2D shadowMap, sampler shadowMapSampler)
+{
+	const Vec3 dir = -frag2Light;
+	const Vec3 dirabs = abs(dir);
+	const F32 dist = max(dirabs.x, max(dirabs.y, dirabs.z));
+
+	// 1) Project the dist to light's proj mat
+	//
+	const F32 near = CLUSTER_OBJECT_FRUSTUM_NEAR_PLANE;
+	const F32 far = light.m_radius;
+	const F32 g = near - far;
+
+	const F32 zVSpace = -dist;
+	const F32 w = -zVSpace;
+	F32 z = (far * zVSpace + far * near) / g;
+	z /= w;
+
+	// 2) Read shadow tex
+	//
+
+	// Convert cube coords
+	U32 faceIdxu;
+	Vec2 uv = convertCubeUvsu(dir, faceIdxu);
+
+	// Get the atlas offset
+	Vec2 atlasOffset;
+	atlasOffset.x = light.m_shadowAtlasTileOffsets[faceIdxu >> 1u][(faceIdxu & 1u) << 1u];
+	atlasOffset.y = light.m_shadowAtlasTileOffsets[faceIdxu >> 1u][((faceIdxu & 1u) << 1u) + 1u];
+
+	// Compute UV
+	uv = fma(uv, Vec2(light.m_shadowAtlasTileScale), atlasOffset);
+
+	// Sample
+	const Vec4 shadowMoments = textureLod(shadowMap, shadowMapSampler, uv, 0.0);
+
+	// 3) Compare
+	//
+	const F32 shadowFactor = evsmComputeShadowFactor(z, shadowMoments);
+
+	return shadowFactor;
+}
+
+// Compute the shadow factor of a directional light
+F32 computeShadowFactorDirLight(DirectionalLight2 light, U32 cascadeIdx, Vec3 worldPos, texture2D shadowMap,
+								sampler shadowMapSampler)
+{
+#define ANKI_FAST_CASCADES_WORKAROUND 1 // Doesn't make sense but it's super fast
+
+#if ANKI_FAST_CASCADES_WORKAROUND
+	// Assumes MAX_SHADOW_CASCADES is 4
+	Mat4 lightProjectionMat;
+	switch(cascadeIdx)
+	{
+	case 0:
+		lightProjectionMat = light.m_textureMatrices[0];
+		break;
+	case 1:
+		lightProjectionMat = light.m_textureMatrices[1];
+		break;
+	case 2:
+		lightProjectionMat = light.m_textureMatrices[2];
+		break;
+	default:
+		lightProjectionMat = light.m_textureMatrices[3];
+	}
+#else
+	const Mat4 lightProjectionMat = light.m_textureMatrices[cascadeIdx];
+#endif
+
+	const Vec4 texCoords4 = lightProjectionMat * Vec4(worldPos, 1.0);
+	const Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+
+	const Vec4 shadowMoments = textureLod(shadowMap, shadowMapSampler, texCoords3.xy, 0.0);
+
+	return evsmComputeShadowFactor(texCoords3.z, shadowMoments);
+}
+
+// Compute the shadow factor of a directional light
+F32 computeShadowFactorDirLight(Mat4 lightProjectionMat, Vec3 worldPos, texture2D shadowMap,
+								samplerShadow shadowMapSampler)
+{
+	const Vec4 texCoords4 = lightProjectionMat * Vec4(worldPos, 1.0);
+	const Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+
+	const F32 shadowFactor = textureLod(shadowMap, shadowMapSampler, texCoords3, 0.0);
+	return shadowFactor;
+}
+
+// Compute the cubemap texture lookup vector given the reflection vector (r) the radius squared of the probe (R2) and
+// the frag pos in sphere space (f)
+Vec3 computeCubemapVecAccurate(Vec3 r, F32 R2, Vec3 f)
+{
+	// Compute the collision of the r to the inner part of the sphere
+	// From now on we work on the sphere's space
+
+	// Project the center of the sphere (it's zero now since we are in sphere space) in ray "f,r"
+	const Vec3 p = f - r * dot(f, r);
+
+	// The collision to the sphere is point x where x = p + T * r
+	// Because of the pythagorean theorem: R^2 = dot(p, p) + dot(T * r, T * r)
+	// solving for T, T = R / |p|
+	// then x becomes x = sqrt(R^2 - dot(p, p)) * r + p;
+	F32 pp = dot(p, p);
+	pp = min(pp, R2);
+	const F32 sq = sqrt(R2 - pp);
+	const Vec3 x = p + sq * r;
+
+	return x;
+}
+
+// Cheap version of computeCubemapVecAccurate
+Vec3 computeCubemapVecCheap(Vec3 r, F32 R2, Vec3 f)
+{
+	return r;
+}
+
+F32 computeAttenuationFactor(F32 squareRadiusOverOne, Vec3 frag2Light)
+{
+	const F32 fragLightDist = dot(frag2Light, frag2Light);
+	F32 att = 1.0 - fragLightDist * squareRadiusOverOne;
+	att = max(0.0, att);
+	return att * att;
+}
+
+// Given the probe properties trace a ray inside the probe and find the cube tex coordinates to sample
+Vec3 intersectProbe(Vec3 fragPos, // Ray origin
+					Vec3 rayDir, // Ray direction
+					Vec3 probeAabbMin, Vec3 probeAabbMax,
+					Vec3 probeOrigin // Cubemap origin
+)
+{
+	// Compute the intersection point
+	const F32 intresectionDist = testRayAabbInside(fragPos, rayDir, probeAabbMin, probeAabbMax);
+	const Vec3 intersectionPoint = fragPos + intresectionDist * rayDir;
+
+	// Compute the cubemap vector
+	return intersectionPoint - probeOrigin;
+}
+
+// Compute a weight (factor) of fragPos against some probe's bounds. The weight will be zero when fragPos is close to
+// AABB bounds and 1.0 at fadeDistance and less.
+F32 computeProbeBlendWeight(Vec3 fragPos, // Doesn't need to be inside the AABB
+							Vec3 probeAabbMin, Vec3 probeAabbMax, F32 fadeDistance)
+{
+	// Compute the min distance of fragPos from the edges of the AABB
+	const Vec3 distFromMin = fragPos - probeAabbMin;
+	const Vec3 distFromMax = probeAabbMax - fragPos;
+	const Vec3 minDistVec = min(distFromMin, distFromMax);
+	const F32 minDist = min(minDistVec.x, min(minDistVec.y, minDistVec.z));
+
+	// Use saturate because minDist might be negative.
+	return saturate(minDist / fadeDistance);
+}
+
+// Given the value of the 6 faces of the dice and a normal, sample the correct weighted value.
+// https://www.shadertoy.com/view/XtcBDB
+Vec3 sampleAmbientDice(Vec3 posx, Vec3 negx, Vec3 posy, Vec3 negy, Vec3 posz, Vec3 negz, Vec3 normal)
+{
+	const Vec3 axisWeights = abs(normal);
+	const Vec3 uv = NDC_TO_UV(normal);
+
+	Vec3 col = mix(negx, posx, uv.x) * axisWeights.x;
+	col += mix(negy, posy, uv.y) * axisWeights.y;
+	col += mix(negz, posz, uv.z) * axisWeights.z;
+
+	// Divide by weight
+	col /= axisWeights.x + axisWeights.y + axisWeights.z + EPSILON;
+
+	return col;
+}
+
+// Sample the irradiance term from the clipmap
+Vec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe2 probe,
+							  texture3D textures[MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES2],
+							  sampler linearAnyClampSampler)
+{
+	// Find the UVW
+	Vec3 uvw = (worldPos - probe.m_aabbMin) / (probe.m_aabbMax - probe.m_aabbMin);
+
+	// The U contains the 6 directions so divide
+	uvw.x /= 6.0;
+
+	// Calmp it to avoid direction leaking
+	uvw.x = clamp(uvw.x, probe.m_halfTexelSizeU, (1.0 / 6.0) - probe.m_halfTexelSizeU);
+
+	// Read the irradiance
+	Vec3 irradiancePerDir[6u];
+	ANKI_UNROLL for(U32 dir = 0u; dir < 6u; ++dir)
+	{
+		// Point to the correct UV
+		Vec3 shiftedUVw = uvw;
+		shiftedUVw.x += (1.0 / 6.0) * F32(dir);
+
+		irradiancePerDir[dir] =
+			textureLod(textures[nonuniformEXT(probe.m_textureIndex)], linearAnyClampSampler, shiftedUVw, 0.0).rgb;
+	}
+
+	// Sample the irradiance
+	const Vec3 irradiance = sampleAmbientDice(irradiancePerDir[0], irradiancePerDir[1], irradiancePerDir[2],
+											  irradiancePerDir[3], irradiancePerDir[4], irradiancePerDir[5], normal);
+
+	return irradiance;
+}
+
+U32 computeShadowCascadeIndex(F32 distance, F32 p, F32 effectiveShadowDistance, U32 shadowCascadeCount)
+{
+	const F32 shadowCascadeCountf = F32(shadowCascadeCount);
+	F32 idx = pow(distance / effectiveShadowDistance, 1.0f / p) * shadowCascadeCountf;
+	idx = min(idx, shadowCascadeCountf - 1.0f);
+	return U32(idx);
+}

+ 1 - 1
AnKi/Shaders/LightShading.ankiprog

@@ -241,7 +241,7 @@ void main()
 	{
 	{
 		Vec3 diffIndirect;
 		Vec3 diffIndirect;
 
 
-		if(cluster.m_giProbesMask != 0u)
+		if(bitCount(cluster.m_giProbesMask) == 1)
 		{
 		{
 			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
 			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
 
 

+ 15 - 11
AnKi/Shaders/VolumetricFogAccumulation.ankiprog

@@ -4,7 +4,8 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 ANKI_SPECIALIZATION_CONSTANT_UVEC3(VOLUME_SIZE, 0, UVec3(1));
 ANKI_SPECIALIZATION_CONSTANT_UVEC3(VOLUME_SIZE, 0, UVec3(1));
-ANKI_SPECIALIZATION_CONSTANT_U32(FINAL_CLUSTER_Z, 9, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(FINAL_Z_SPLIT, 3, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(Z_SPLIT_COUNT, 4, 1);
 
 
 #pragma anki start comp
 #pragma anki start comp
 
 
@@ -24,10 +25,9 @@ layout(push_constant, std430) uniform pc_
 	F32 u_fogScatteringCoeff;
 	F32 u_fogScatteringCoeff;
 	F32 u_fogAbsorptionCoeff;
 	F32 u_fogAbsorptionCoeff;
 	F32 u_density;
 	F32 u_density;
-	F32 u_padding0;
+	F32 u_near;
 	Vec3 u_fogDiffuse;
 	Vec3 u_fogDiffuse;
-	U32 u_padding1;
-	ClustererMagicValues u_clustererMagic;
+	F32 u_far;
 };
 };
 
 
 void main()
 void main()
@@ -42,20 +42,24 @@ void main()
 	Vec4 colorAndDensityFront = Vec4(0.0);
 	Vec4 colorAndDensityFront = Vec4(0.0);
 	ANKI_LOOP for(U32 i = 0u; i < VOLUME_SIZE.z; ++i)
 	ANKI_LOOP for(U32 i = 0u; i < VOLUME_SIZE.z; ++i)
 	{
 	{
-		// Compute the cluster K limits of this cluster fragment
-		const F32 clusterKNear = F32(i) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
-		const F32 clusterKFar = F32(i + 1u) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
+		const F32 fi = F32(i);
+
+		// Compute the linear depth
+		const F32 maxLinearDepth = F32(FINAL_Z_SPLIT + 1u) / F32(Z_SPLIT_COUNT);
+		const F32 linearDepthFraction = maxLinearDepth / F32(VOLUME_SIZE.z);
+		const F32 linearDepthNear = fi * linearDepthFraction;
+		const F32 linearDepthFar = (fi + 1.0) * linearDepthFraction;
 
 
 		// Compute the min and max Z in view space if this cluster fragment
 		// Compute the min and max Z in view space if this cluster fragment
-		const F32 zVSpaceNear = -computeClusterNearf(u_clustererMagic, clusterKNear);
-		const F32 zVSpaceFar = -computeClusterNearf(u_clustererMagic, clusterKFar);
+		const F32 zVSpaceNear = -linearDepthNear * (u_far - u_near) + u_near;
+		const F32 zVSpaceFar = -linearDepthFar * (u_far - u_near) + u_near;
 
 
 		// Compute the thikness of this fragment
 		// Compute the thikness of this fragment
 		const F32 layerThinkness = abs(zVSpaceNear - zVSpaceFar);
 		const F32 layerThinkness = abs(zVSpaceNear - zVSpaceFar);
 
 
 		// Read the light value and the fog density from the fog volumes
 		// Read the light value and the fog density from the fog volumes
-		Vec4 lightAndFogDensity =
-			textureLod(u_lightVolume, u_linearAnyClampSampler, Vec3(uv, clusterKFar / F32(FINAL_CLUSTER_Z + 1u)), 0.0);
+		const F32 w = (fi + 0.5) / F32(VOLUME_SIZE.z);
+		Vec4 lightAndFogDensity = textureLod(u_lightVolume, u_linearAnyClampSampler, Vec3(uv, w), 0.0);
 		lightAndFogDensity.xyz *= u_fogDiffuse / PI;
 		lightAndFogDensity.xyz *= u_fogDiffuse / PI;
 		lightAndFogDensity.w += u_density; // Apply the default density
 		lightAndFogDensity.w += u_density; // Apply the default density
 
 

+ 92 - 91
AnKi/Shaders/VolumetricLightingAccumulation.ankiprog

@@ -8,10 +8,9 @@
 #pragma anki mutator ENABLE_SHADOWS 0 1
 #pragma anki mutator ENABLE_SHADOWS 0 1
 
 
 ANKI_SPECIALIZATION_CONSTANT_UVEC3(VOLUME_SIZE, 0, UVec3(1));
 ANKI_SPECIALIZATION_CONSTANT_UVEC3(VOLUME_SIZE, 0, UVec3(1));
-ANKI_SPECIALIZATION_CONSTANT_UVEC3(CLUSTER_COUNT, 3, UVec3(1));
-ANKI_SPECIALIZATION_CONSTANT_UVEC3(FRACTION, 6, UVec3(1));
-ANKI_SPECIALIZATION_CONSTANT_UVEC3(NOISE_TEX_SIZE, 9, UVec3(1));
-ANKI_SPECIALIZATION_CONSTANT_U32(FINAL_CLUSTER_Z, 12, 1);
+ANKI_SPECIALIZATION_CONSTANT_UVEC2(TILE_COUNT, 3, UVec2(1));
+ANKI_SPECIALIZATION_CONSTANT_U32(Z_SPLIT_COUNT, 5, 1);
+ANKI_SPECIALIZATION_CONSTANT_U32(FINAL_Z_SPLIT, 6, 1);
 
 
 #pragma anki start comp
 #pragma anki start comp
 
 
@@ -24,52 +23,50 @@ layout(set = 0, binding = 0) uniform sampler u_linearAnyRepeatSampler;
 layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
 layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
 
 
 layout(set = 0, binding = 2) writeonly uniform image3D u_volume;
 layout(set = 0, binding = 2) writeonly uniform image3D u_volume;
-layout(set = 0, binding = 3) uniform texture3D u_noiseTex;
+layout(set = 0, binding = 3) uniform texture2D u_noiseTex;
 layout(set = 0, binding = 4) uniform texture3D u_prevVolume;
 layout(set = 0, binding = 4) uniform texture3D u_prevVolume;
 
 
-layout(push_constant, std430) uniform pc_
-{
-	Vec3 u_padding;
-	F32 u_noiseOffset;
-};
-
-#define LIGHT_SET 0
-#define LIGHT_COMMON_UNIS_BINDING 5
-#define LIGHT_LIGHTS_BINDING 6
-#define LIGHT_GLOBAL_ILLUMINATION_BINDING 9
-#define LIGHT_FOG_DENSITY_VOLUMES_BINDING 11
-#define LIGHT_CLUSTERS_BINDING 12
-#include <AnKi/Shaders/ClusteredShadingCommon.glsl>
+#define CLUSTERED_SHADING_SET 0
+#define CLUSTERED_SHADING_UNIFORMS_BINDING 5
+#define CLUSTERED_SHADING_LIGHTS_BINDING 6
+#define CLUSTERED_SHADING_GI_BINDING 9
+#define CLUSTERED_SHADING_FOG_BINDING 11
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 12
+#include <AnKi/Shaders/ClusteredShadingCommon2.glsl>
 
 
 Vec3 g_globalInvocationID = Vec3(gl_GlobalInvocationID);
 Vec3 g_globalInvocationID = Vec3(gl_GlobalInvocationID);
 
 
 Vec3 readRand()
 Vec3 readRand()
 {
 {
-	Vec3 uv = (g_globalInvocationID + 0.5) / Vec3(NOISE_TEX_SIZE);
-	uv.z += u_noiseOffset;
-	return textureLod(u_noiseTex, u_linearAnyRepeatSampler, uv, 0.0).rgb;
+	Vec3 random = textureLod(u_noiseTex, u_linearAnyRepeatSampler, (g_globalInvocationID.xy + 0.5) / 64.0, 0.0).rgb;
+	random = animateBlueNoise(random, u_clusterShading.m_frame);
+
+	return random;
 }
 }
 
 
-Vec3 worldPosInsideClusterAndZViewSpace(Vec3 relativePos, out F32 negativeZViewSpace)
+Vec3 worldPosInsideClusterAndZViewSpace(Vec3 relativePos, out F32 negativeZViewSpace, out Vec3 uvw)
 {
 {
-	// Compute the cluster Z as float
-	const F32 clusterKNear = g_globalInvocationID.z * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
-	const F32 clusterKFar = (g_globalInvocationID.z + 1.0) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
-	const F32 clusterK = mix(clusterKNear, clusterKFar, relativePos.z);
-
-	// Get a Z value
-	negativeZViewSpace = computeClusterNearf(u_clustererMagic, clusterK);
-	const F32 zVSpace = -negativeZViewSpace;
-
-	// Get a XY value
+	// Compute the linear depth
+	const F32 maxLinearDepth = F32(FINAL_Z_SPLIT + 1u) / F32(Z_SPLIT_COUNT);
+	const F32 linearDepthFraction = maxLinearDepth / F32(VOLUME_SIZE.z);
+	const F32 linearDepthNear = g_globalInvocationID.z * linearDepthFraction;
+	const F32 linearDepthFar = (g_globalInvocationID.z + 1.0) * linearDepthFraction;
+	const F32 linearDepth = mix(linearDepthNear, linearDepthFar, relativePos.z);
+	uvw.z = linearDepth;
+
+	// Z view space
+	negativeZViewSpace = linearDepth * (u_clusterShading.m_far - u_clusterShading.m_near) + u_clusterShading.m_near;
+	const F32 zViewSpace = -negativeZViewSpace;
+
+	// Get the position in view space
 	const Vec2 uvMin = g_globalInvocationID.xy / Vec2(VOLUME_SIZE.xy);
 	const Vec2 uvMin = g_globalInvocationID.xy / Vec2(VOLUME_SIZE.xy);
 	const Vec2 uvMax = uvMin + 1.0 / Vec2(VOLUME_SIZE.xy);
 	const Vec2 uvMax = uvMin + 1.0 / Vec2(VOLUME_SIZE.xy);
 	const Vec2 uv = mix(uvMin, uvMax, relativePos.xy);
 	const Vec2 uv = mix(uvMin, uvMax, relativePos.xy);
-	const Vec2 ndc = UV_TO_NDC(uv);
-	const Vec2 xyZVspace = ndc * u_unprojectionParams.xy * zVSpace;
+	uvw.xy = uv;
+	const Vec2 xyViewSpace = UV_TO_NDC(uv) * u_clusterShading.m_matrices.m_unprojectionParameters.xy * zViewSpace;
 
 
-	// Get the pos
-	const Vec4 worldPos4 = u_invViewMat * Vec4(xyZVspace, zVSpace, 1.0);
+	// Get the final world pos
+	const Vec4 worldPos4 = u_clusterShading.m_matrices.m_invertedView * Vec4(xyViewSpace, zViewSpace, 1.0);
 	const Vec3 worldPos = worldPos4.xyz;
 	const Vec3 worldPos = worldPos4.xyz;
 
 
 	return worldPos;
 	return worldPos;
@@ -78,7 +75,8 @@ Vec3 worldPosInsideClusterAndZViewSpace(Vec3 relativePos, out F32 negativeZViewS
 Vec3 worldPosInsideCluster(Vec3 relativePos)
 Vec3 worldPosInsideCluster(Vec3 relativePos)
 {
 {
 	F32 unused;
 	F32 unused;
-	return worldPosInsideClusterAndZViewSpace(relativePos, unused);
+	Vec3 unused1;
+	return worldPosInsideClusterAndZViewSpace(relativePos, unused, unused1);
 }
 }
 
 
 // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html
 // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter16.html
@@ -94,39 +92,38 @@ F32 phaseFunction(Vec3 viewDir, Vec3 lightDir, F32 g)
 	return saturate(a * b);
 	return saturate(a * b);
 }
 }
 
 
-Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpace)
+Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpace)
 {
 {
 	Vec3 color = Vec3(0.0);
 	Vec3 color = Vec3(0.0);
-	const Vec3 viewDir = normalize(u_cameraPos - worldPos);
-
-	// Get ID offset
-	U32 idxOffset = u_clusters[clusterIdx];
+	const Vec3 viewDir = normalize(u_clusterShading.m_cameraPosition - worldPos);
 
 
 	// Dir light
 	// Dir light
-	if(u_dirLight.m_active != 0u)
+	const DirectionalLight2 dirLight = u_clusterShading.m_directionalLight;
+	if(dirLight.m_active != 0u)
 	{
 	{
-		F32 factor = phaseFunction(viewDir, u_dirLight.m_dir, PHASE_FUNCTION_ANISOTROPY);
+		F32 factor = phaseFunction(viewDir, dirLight.m_direction, PHASE_FUNCTION_ANISOTROPY);
 
 
 #if ENABLE_SHADOWS
 #if ENABLE_SHADOWS
-		if(u_dirLight.m_cascadeCount > 0u && negativeZViewSpace < u_dirLight.m_effectiveShadowDistance)
+		if(dirLight.m_cascadeCount > 0u && negativeZViewSpace < dirLight.m_effectiveShadowDistance)
 		{
 		{
 			const U32 cascadeIdx =
 			const U32 cascadeIdx =
-				computeShadowCascadeIndex(negativeZViewSpace, u_dirLight.m_shadowCascadesDistancePower,
-										  u_dirLight.m_effectiveShadowDistance, u_dirLight.m_cascadeCount);
+				computeShadowCascadeIndex(negativeZViewSpace, dirLight.m_shadowCascadesDistancePower,
+										  dirLight.m_effectiveShadowDistance, dirLight.m_cascadeCount);
 
 
 			factor *=
 			factor *=
-				computeShadowFactorDirLight(u_dirLight, cascadeIdx, worldPos, u_shadowTex, u_linearAnyClampSampler);
+				computeShadowFactorDirLight(dirLight, cascadeIdx, worldPos, u_shadowAtlasTex, u_linearAnyClampSampler);
 		}
 		}
 #endif
 #endif
 
 
-		color += u_dirLight.m_diffuseColor * factor;
+		color += dirLight.m_diffuseColor * factor;
 	}
 	}
 
 
 	// Point lights
 	// Point lights
-	U32 idx;
-	ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+	ANKI_LOOP while(cluster.m_pointLightsMask != 0u)
 	{
 	{
-		const PointLight light = u_pointLights[idx];
+		const I32 idx = findLSB64(cluster.m_pointLightsMask);
+		cluster.m_pointLightsMask &= ~(1ul << U64(idx));
+		const PointLight2 light = u_pointLights2[idx];
 
 
 		const Vec3 frag2Light = light.m_position - worldPos;
 		const Vec3 frag2Light = light.m_position - worldPos;
 		F32 factor = computeAttenuationFactor(light.m_squareRadiusOverOne, frag2Light);
 		F32 factor = computeAttenuationFactor(light.m_squareRadiusOverOne, frag2Light);
@@ -136,7 +133,7 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpac
 #if ENABLE_SHADOWS
 #if ENABLE_SHADOWS
 		if(light.m_shadowAtlasTileScale >= 0.0)
 		if(light.m_shadowAtlasTileScale >= 0.0)
 		{
 		{
-			factor *= computeShadowFactorPointLight(light, frag2Light, u_shadowTex, u_linearAnyClampSampler);
+			factor *= computeShadowFactorPointLight(light, frag2Light, u_shadowAtlasTex, u_linearAnyClampSampler);
 		}
 		}
 #endif
 #endif
 
 
@@ -144,24 +141,25 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpac
 	}
 	}
 
 
 	// Spot lights
 	// Spot lights
-	ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+	ANKI_LOOP while(cluster.m_spotLightsMask != 0u)
 	{
 	{
-		const SpotLight light = u_spotLights[idx];
+		const I32 idx = findLSB64(cluster.m_spotLightsMask);
+		cluster.m_spotLightsMask &= ~(1ul << U64(idx));
+		const SpotLight2 light = u_spotLights2[idx];
 
 
 		const Vec3 frag2Light = light.m_position - worldPos;
 		const Vec3 frag2Light = light.m_position - worldPos;
 		F32 factor = computeAttenuationFactor(light.m_squareRadiusOverOne, frag2Light);
 		F32 factor = computeAttenuationFactor(light.m_squareRadiusOverOne, frag2Light);
 
 
 		const Vec3 l = normalize(frag2Light);
 		const Vec3 l = normalize(frag2Light);
 
 
-		factor *= computeSpotFactor(l, light.m_outerCos, light.m_innerCos, light.m_dir);
+		factor *= computeSpotFactor(l, light.m_outerCos, light.m_innerCos, light.m_direction);
 
 
-		factor *= phaseFunction(viewDir, light.m_dir, PHASE_FUNCTION_ANISOTROPY);
+		factor *= phaseFunction(viewDir, light.m_direction, PHASE_FUNCTION_ANISOTROPY);
 
 
 #if ENABLE_SHADOWS
 #if ENABLE_SHADOWS
-		const F32 shadowmapLayerIdx = light.m_shadowmapId;
-		if(shadowmapLayerIdx >= 0.0)
+		if(light.m_shadowLayer != MAX_U32)
 		{
 		{
-			factor *= computeShadowFactorSpotLight(light, worldPos, u_shadowTex, u_linearAnyClampSampler);
+			factor *= computeShadowFactorSpotLight(light, worldPos, u_shadowAtlasTex, u_linearAnyClampSampler);
 		}
 		}
 #endif
 #endif
 
 
@@ -170,15 +168,13 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpac
 
 
 	// Indirect diffuse GI
 	// Indirect diffuse GI
 	{
 	{
-		idxOffset = u_clusters[clusterIdx];
-		idxOffset = u_lightIndices[idxOffset - 3u];
 		Vec3 diffIndirect;
 		Vec3 diffIndirect;
 
 
-		if(subgroupAll(u_lightIndices[idxOffset] != MAX_U32 && u_lightIndices[idxOffset + 1u] == MAX_U32))
+		if(bitCount(cluster.m_giProbesMask) == 1)
 		{
 		{
 			// Only one probe, do a fast path without blend weight
 			// Only one probe, do a fast path without blend weight
 
 
-			GlobalIlluminationProbe probe = u_giProbes[u_lightIndices[idxOffset]];
+			const GlobalIlluminationProbe2 probe = u_giProbes[findLSB(cluster.m_giProbesMask)];
 
 
 			// Sample
 			// Sample
 			diffIndirect = sampleGlobalIllumination(worldPos, viewDir, probe, u_globalIlluminationTextures,
 			diffIndirect = sampleGlobalIllumination(worldPos, viewDir, probe, u_globalIlluminationTextures,
@@ -192,9 +188,11 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpac
 			diffIndirect = Vec3(0.0);
 			diffIndirect = Vec3(0.0);
 
 
 			// Loop probes
 			// Loop probes
-			ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+			ANKI_LOOP while(cluster.m_giProbesMask != 0u)
 			{
 			{
-				GlobalIlluminationProbe probe = u_giProbes[idx];
+				const U32 idx = U32(findLSB(cluster.m_giProbesMask));
+				cluster.m_giProbesMask &= ~(1u << idx);
+				const GlobalIlluminationProbe2 probe = u_giProbes[idx];
 
 
 				// Compute blend weight
 				// Compute blend weight
 				const F32 blendWeight =
 				const F32 blendWeight =
@@ -219,11 +217,11 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 negativeZViewSpac
 	// Fog density
 	// Fog density
 	F32 fogDensity = 0.0;
 	F32 fogDensity = 0.0;
 	{
 	{
-		idxOffset = u_clusters[clusterIdx];
-		idxOffset = u_lightIndices[idxOffset - 1u];
-		ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+		ANKI_LOOP while(cluster.m_fogDensityVolumesMask != 0u)
 		{
 		{
-			const FogDensityVolume vol = u_fogDensityVolumes[idx];
+			const U32 idx = U32(findLSB(cluster.m_fogDensityVolumesMask));
+			cluster.m_fogDensityVolumesMask &= ~(1u << idx);
+			const FogDensityVolume2 vol = u_fogDensityVolumes[idx];
 
 
 			F32 factor;
 			F32 factor;
 			ANKI_BRANCH if(vol.m_isBox == 1u)
 			ANKI_BRANCH if(vol.m_isBox == 1u)
@@ -253,40 +251,43 @@ void main()
 		return;
 		return;
 	}
 	}
 
 
-	// Find the cluster
-	const UVec3 clusterXYZ = gl_GlobalInvocationID / FRACTION;
-	const U32 clusterIdx =
-		clusterXYZ.z * (CLUSTER_COUNT.x * CLUSTER_COUNT.y) + clusterXYZ.y * CLUSTER_COUNT.x + clusterXYZ.x;
-
 	// Find a random pos inside the cluster
 	// Find a random pos inside the cluster
 	F32 negativeZViewSpace;
 	F32 negativeZViewSpace;
-	const Vec3 worldPos = worldPosInsideClusterAndZViewSpace(readRand(), negativeZViewSpace);
+	Vec3 uvw;
+	const Vec3 worldPos = worldPosInsideClusterAndZViewSpace(readRand(), negativeZViewSpace, uvw);
+
+	// Get the cluster
+	const UVec2 tileIdxXY = UVec2(uvw.xy * F32(TILE_COUNT));
+	const U32 tileIdx = tileIdxXY.y * TILE_COUNT.x + tileIdxXY.x;
+	Cluster cluster = u_clusters2[tileIdx];
+
+	const U32 zSplitIdx = U32(uvw.z * F32(Z_SPLIT_COUNT));
+	const Cluster split = u_clusters2[TILE_COUNT.x * TILE_COUNT.y + zSplitIdx];
+
+	cluster.m_pointLightsMask |= split.m_pointLightsMask;
+	cluster.m_spotLightsMask |= split.m_spotLightsMask;
+	cluster.m_giProbesMask |= split.m_giProbesMask;
+	cluster.m_fogDensityVolumesMask |= split.m_fogDensityVolumesMask;
 
 
 	// Get lighting
 	// Get lighting
-	Vec4 lightAndFog = accumulateLightsAndFog(clusterIdx, worldPos, negativeZViewSpace);
+	Vec4 lightAndFog = accumulateLightsAndFog(cluster, worldPos, negativeZViewSpace);
 
 
 	// Read the prev result
 	// Read the prev result
 	{
 	{
 		// Better get a new world pos in the center of the cluster. Using worldPos creates noisy results
 		// Better get a new world pos in the center of the cluster. Using worldPos creates noisy results
 		const Vec3 midWPos = worldPosInsideCluster(Vec3(0.5));
 		const Vec3 midWPos = worldPosInsideCluster(Vec3(0.5));
 
 
-		// Compute UV
-		const Vec4 prevClipPos4 = u_prevViewProjMat * Vec4(midWPos, 1.0);
-		const Vec2 prevUv = NDC_TO_UV(prevClipPos4.xy / prevClipPos4.w);
-
-		// Compute new Z tex coord
-		F32 k = computeClusterKf(u_prevClustererMagic, midWPos);
-		k /= F32(FINAL_CLUSTER_Z + 1u);
+		// Project
+		const Vec4 prevClipPos4 = u_clusterShading.m_previousMatrices.m_viewProjection * Vec4(midWPos, 1.0);
+		const Vec3 prevClipPos = prevClipPos4.xyz / prevClipPos4.w;
+		const F32 linearDepth = linearizeDepth(prevClipPos.z, u_clusterShading.m_near, u_clusterShading.m_far);
 
 
 		// Read prev
 		// Read prev
-		const Vec3 uvw = Vec3(prevUv, k);
-		const Vec3 ndc = UV_TO_NDC(uvw);
-		if(all(lessThan(abs(ndc), Vec3(1.0))))
+		if(all(greaterThan(prevClipPos, Vec3(-1.0))) && all(lessThan(prevClipPos, Vec3(1.0))))
 		{
 		{
-			const Vec4 prev = textureLod(u_prevVolume, u_linearAnyClampSampler, uvw, 0.0);
-
-			// Modulate
-			lightAndFog = mix(prev, lightAndFog, 1.0 / 16.0);
+			const Vec3 uvw = Vec3(NDC_TO_UV(prevClipPos.xy), linearDepth);
+			const Vec4 history = textureLod(u_prevVolume, u_linearAnyClampSampler, uvw, 0.0);
+			lightAndFog = mix(history, lightAndFog, 1.0 / 16.0);
 		}
 		}
 	}
 	}