Browse Source

More GI work

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
dcc720faab

+ 8 - 0
shaders/ClusteredShadingCommon.glsl

@@ -94,6 +94,14 @@ layout(std140, row_major, set = LIGHT_SET, binding = LIGHT_FOG_DENSITY_VOLUMES_B
 };
 #endif
 
+//
+// GI clipmap
+//
+#if defined(LIGHT_GLOBAL_ILLUMINATION_BINDING)
+layout(set = LIGHT_SET, binding = LIGHT_GLOBAL_ILLUMINATION_BINDING) uniform texture3D
+	u_globalIlluminationTex[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT * 6u];
+#endif
+
 //
 // Cluster uniforms
 //

+ 30 - 3
shaders/Functions.glsl

@@ -419,7 +419,7 @@ Vec3 readErosion(texture2D tex, sampler sampl, const Vec2 uv)
 }
 
 // 5 color heatmap from a factor.
-Vec3 heatmap(F32 factor)
+Vec3 heatmap(const F32 factor)
 {
 	F32 intPart;
 	const F32 fractional = modf(factor * 4.0, intPart);
@@ -442,12 +442,39 @@ Vec3 heatmap(F32 factor)
 	}
 }
 
-Bool incorrectColor(Vec3 c)
+// Return a color per cubemap face. The +X is red, -X dark red, +Y green, -Y dark green, +Z blue, -Z dark blue
+Vec3 colorPerCubeFace(const U32 dir)
+{
+	Vec3 color;
+	switch(dir)
+	{
+	case 0:
+		color = Vec3(1.0, 0.0, 0.0);
+		break;
+	case 1:
+		color = Vec3(0.5, 0.0, 0.0);
+		break;
+	case 2:
+		color = Vec3(0.0, 1.0, 0.0);
+		break;
+	case 3:
+		color = Vec3(0.0, 0.5, 0.0);
+		break;
+	case 4:
+		color = Vec3(0.0, 0.0, 1.0);
+		break;
+	default:
+		color = Vec3(0.0, 0.0, 0.5);
+	}
+	return color;
+}
+
+Bool incorrectColor(const Vec3 c)
 {
 	return isnan(c.x) || isnan(c.y) || isnan(c.z) || isinf(c.x) || isinf(c.y) || isinf(c.z);
 }
 
-F32 areaElement(F32 x, F32 y)
+F32 areaElement(const F32 x, const F32 y)
 {
 	return atan(x * y, sqrt(x * x + y * y + 1.0));
 }

+ 15 - 5
shaders/GlobalIlluminationClipmapPopulation.glslp

@@ -5,7 +5,6 @@
 
 // Populates a clipmap with the irradiance values of probes.
 
-#pragma anki input const U32 CLIPMAP_LEVEL_COUNT
 #pragma anki input const UVec3 WORKGROUP_SIZE
 #pragma anki input const U32 PROBE_COUNT
 #pragma anki mutator HAS_PROBES 0 1
@@ -15,6 +14,8 @@
 #include <shaders/glsl_cpp_common/ClusteredShading.h>
 #include <shaders/Functions.glsl>
 
+#define DEBUG_MODE 0 // 0: disabled, 1: draw different shade per dir per level, 2 draw differend shade per cell
+
 layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = WORKGROUP_SIZE.z) in;
 
 struct Clipmap
@@ -29,12 +30,12 @@ struct Clipmap
 
 layout(set = 0, binding = 0, std140) uniform ubo_
 {
-	Clipmap u_clipmaps[CLIPMAP_LEVEL_COUNT];
+	Clipmap u_clipmaps[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT];
 	Vec3 u_cameraPos;
 	U32 u_padding;
 };
 
-layout(set = 0, binding = 1) uniform writeonly image3D u_clipmapImages[6u * CLIPMAP_LEVEL_COUNT];
+layout(set = 0, binding = 1) uniform writeonly image3D u_clipmapImages[6u * GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT];
 
 #if HAS_PROBES
 layout(set = 0, binding = 2) buffer ssbo_
@@ -62,7 +63,7 @@ Vec3 computeUvwCoordsInsideProbe(const GlobalIlluminationProbe probe, const Vec3
 void main()
 {
 	// Populate all clipmaps
-	ANKI_UNROLL for(U32 clipmapIdx = 0u; clipmapIdx < CLIPMAP_LEVEL_COUNT; ++clipmapIdx)
+	ANKI_UNROLL for(U32 clipmapIdx = 0u; clipmapIdx < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++clipmapIdx)
 	{
 		const Clipmap clipmap = u_clipmaps[clipmapIdx];
 
@@ -112,7 +113,16 @@ void main()
 		{
 			const U32 clipmapImageIdx = clipmapIdx * 6u + dirIdx;
 
-#if HAS_PROBES
+#if DEBUG_MODE == 1
+			const Vec3 dirColor = colorPerCubeFace(dirIdx);
+			const F32 clipmapFactor = F32(GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT - clipmapIdx)
+									  / F32(GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT);
+			const Vec3 storedColor = dirColor * clipmapFactor;
+#elif DEBUG_MODE == 2
+			const F32 factor =
+				F32(gl_LocalInvocationIndex) / F32(WORKGROUP_SIZE.x * WORKGROUP_SIZE.y * WORKGROUP_SIZE.z);
+			const Vec3 storedColor = heatmap(factor);
+#elif HAS_PROBES
 			const Vec3 storedColor = accumulatedIrradiance[dirIdx] / weight;
 #else
 			const Vec3 storedColor = Vec3(0.0);

+ 6 - 26
shaders/IrradianceDice.glslp

@@ -22,12 +22,12 @@ layout(set = 0, binding = 1) uniform texture2D u_lightShadingTex;
 layout(set = 0, binding = 2) uniform texture2D u_gbufferTex0;
 layout(set = 0, binding = 3) uniform texture2D u_gbufferTex1;
 layout(set = 0, binding = 4) uniform texture2D u_gbufferTex2;
-layout(set = 0, binding = 5) uniform writeonly image3D u_irradianceVolumes[6];
+layout(set = 0, binding = 5) uniform writeonly image3D u_irradianceVolumes[6u];
 
 // This is a temporary buffer used instead of shared memory because it's too large
 layout(set = 0, binding = 6) buffer ssbo_
 {
-	Vec3 u_integrationResults[6][INPUT_TEXTURES_HEIGHT * INPUT_TEXTURES_HEIGHT];
+	Vec3 u_integrationResults[6u][INPUT_TEXTURES_HEIGHT * INPUT_TEXTURES_HEIGHT];
 };
 
 layout(push_constant, std430) uniform pc_
@@ -36,7 +36,7 @@ layout(push_constant, std430) uniform pc_
 	I32 u_padding;
 };
 
-shared Vec3 s_diceIrradiance[6];
+shared Vec3 s_diceIrradiance[6u];
 
 void main()
 {
@@ -44,7 +44,7 @@ void main()
 	const Vec2 INPUT_TEXTURES_SIZE = Vec2(INPUT_TEXTURES_HEIGHT * 6u, INPUT_TEXTURES_HEIGHT);
 
 	// Compute the NDC used in cubeCoordSolidAngle
-	const Vec2 faceUv = (Vec2(gl_LocalInvocationID.x, gl_LocalInvocationID.y) + 0.5) / INPUT_TEXTURES_HEIGHT_F;
+	const Vec2 faceUv = (Vec2(gl_LocalInvocationID.xy) + 0.5) / INPUT_TEXTURES_HEIGHT_F;
 	const Vec2 ndc = UV_TO_NDC(faceUv);
 
 	// Initialize
@@ -159,27 +159,7 @@ void main()
 			irradiance /= PI; // Pre-divide
 			const Vec3 toStoreValue = irradiance;
 #elif DEBUG_MODE == 1
-			Vec3 toStoreValue;
-			switch(f)
-			{
-			case 0:
-				toStoreValue = Vec3(1.0, 0.0, 0.0);
-				break;
-			case 1:
-				toStoreValue = Vec3(0.5, 0.0, 0.0);
-				break;
-			case 2:
-				toStoreValue = Vec3(0.0, 1.0, 0.0);
-				break;
-			case 3:
-				toStoreValue = Vec3(0.0, 0.5, 0.0);
-				break;
-			case 4:
-				toStoreValue = Vec3(0.0, 0.0, 1.0);
-				break;
-			default:
-				toStoreValue = Vec3(0.0, 0.0, 0.5);
-			}
+			const Vec3 toStoreValue = colorPerCubeFace(f);
 #else
 			const UVec3 volumeSize = UVec3(imageSize(u_irradianceVolumes[0]));
 			const U32 cellIdx =
@@ -188,7 +168,7 @@ void main()
 			const Vec3 toStoreValue = heatmap(headmapFactor);
 #endif
 
-			imageStore(u_irradianceVolumes[f], u_volumeTexel, Vec4(irradiance, 0.0));
+			imageStore(u_irradianceVolumes[f], u_volumeTexel, Vec4(toStoreValue, 0.0));
 		}
 	}
 }

+ 55 - 0
shaders/LightFunctions.glsl

@@ -271,3 +271,58 @@ Vec3 sampleAmbientDice(Vec3 posx, Vec3 negx, Vec3 posy, Vec3 negy, Vec3 posz, Ve
 
 	return col;
 }
+
+// Given the clipmap bounds find in which level a world position belongs.
+U32 findContainingClipmapLevel(
+	const Vec3 worldPos, const ClipmapLevelInfo clipmapInfos[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT], out Vec3 uvw)
+{
+	U32 idx;
+	ANKI_UNROLL for(U32 level = 0u; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		const Bool isLast = level == GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT - 1u;
+		const Bool isInside =
+			all(lessThan(worldPos, clipmapInfos[level].m_max)) && all(greaterThan(worldPos, clipmapInfos[level].m_min));
+		if(isInside || isLast)
+		{
+			// Found
+			idx = level;
+
+			// Compute the UVW coordinates inside the level
+			uvw = (worldPos - clipmapInfos[level].m_min) / (clipmapInfos[level].m_max - clipmapInfos[level].m_min);
+			break;
+		}
+	}
+
+	return idx;
+}
+
+// Sample the irradiance term from the clipmap
+Vec3 sampleGlobalIllumination(const Vec3 worldPos,
+	const Vec3 normal,
+	texture3D clipmapTexArray[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT * 6u],
+	sampler linearAnyClampSampler,
+	const ClipmapLevelInfo clipmapInfos[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT])
+{
+	// Get the level
+	Vec3 uvw;
+	const U32 level = findContainingClipmapLevel(worldPos, clipmapInfos, uvw);
+
+	// Read the irradiance
+	Vec3 irradiancePerDir[6u];
+	ANKI_UNROLL for(U32 dir = 0u; dir < 6u; ++dir)
+	{
+		irradiancePerDir[dir] =
+			textureLod(clipmapTexArray[nonuniformEXT(level * 6u + dir)], linearAnyClampSampler, uvw, 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;
+}

+ 9 - 8
shaders/LightShading.glslp

@@ -39,17 +39,18 @@ void main()
 #define LIGHT_LIGHTS_BINDING 1
 #define LIGHT_INDIRECT_BINDING 4
 #define LIGHT_CLUSTERS_BINDING 8
+#define LIGHT_GLOBAL_ILLUMINATION_BINDING 10
 #include <shaders/ClusteredShadingCommon.glsl>
 
-layout(set = 0, binding = 10) uniform sampler u_nearestAnyClampSampler;
-layout(set = 0, binding = 11) uniform sampler u_trilinearClampSampler;
+layout(set = 0, binding = 11) uniform sampler u_nearestAnyClampSampler;
+layout(set = 0, binding = 12) uniform sampler u_trilinearClampSampler;
 
-layout(set = 0, binding = 12) uniform texture2D u_msRt0;
-layout(set = 0, binding = 13) uniform texture2D u_msRt1;
-layout(set = 0, binding = 14) uniform texture2D u_msRt2;
-layout(set = 0, binding = 15) uniform texture2D u_msDepthRt;
-layout(set = 0, binding = 16) uniform texture2D u_ssrRt;
-layout(set = 0, binding = 17) uniform texture2D u_ssaoRt;
+layout(set = 0, binding = 13) uniform texture2D u_msRt0;
+layout(set = 0, binding = 14) uniform texture2D u_msRt1;
+layout(set = 0, binding = 15) uniform texture2D u_msRt2;
+layout(set = 0, binding = 16) uniform texture2D u_msDepthRt;
+layout(set = 0, binding = 17) uniform texture2D u_ssrRt;
+layout(set = 0, binding = 18) uniform texture2D u_ssaoRt;
 
 layout(location = 0) in Vec2 in_uv;
 layout(location = 1) in Vec2 in_clusterIJ;

+ 21 - 1
shaders/glsl_cpp_common/ClusteredShading.h

@@ -17,6 +17,7 @@ const F32 INVALID_TEXTURE_INDEX = -1.0f;
 const F32 LIGHT_FRUSTUM_NEAR_PLANE = 0.1f / 4.0f; // The near plane on the shadow map frustums.
 const U32 MAX_SHADOW_CASCADES = 4u;
 const F32 SUBSURFACE_MIN = 0.05f;
+const U32 GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT = 2u; // Global illumination clipmap count.
 
 // See the documentation in the ClustererBin class.
 struct ClustererMagicValues
@@ -113,20 +114,35 @@ struct GlobalIlluminationProbe
 	F32 m_padding;
 };
 
+// Clipmap volume info
+struct ClipmapLevelInfo
+{
+	Vec3 m_min;
+	F32 m_padding0;
+	Vec3 m_max;
+	F32 m_padding1;
+};
+
 // Common uniforms for light shading passes
 struct LightingUniforms
 {
 	Vec4 m_unprojectionParams;
+
 	Vec2 m_rendererSize;
 	F32 m_time;
 	F32 m_near;
+
 	Vec3 m_cameraPos;
 	F32 m_far;
+
 	ClustererMagicValues m_clustererMagicValues;
 	ClustererMagicValues m_prevClustererMagicValues;
+
 	UVec4 m_clusterCount;
+
 	Vec3 m_padding;
 	U32 m_lightVolumeLastCluster;
+
 	Mat4 m_viewMat;
 	Mat4 m_invViewMat;
 	Mat4 m_projMat;
@@ -135,9 +151,13 @@ struct LightingUniforms
 	Mat4 m_invViewProjMat;
 	Mat4 m_prevViewProjMat;
 	Mat4 m_prevViewProjMatMulInvViewProjMat; // Used to re-project previous frames
+
 	DirectionalLight m_dirLight;
+
+	ClipmapLevelInfo m_globalIlluminationClipmapLevels[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT];
 };
-const U32 SIZEOF_LIGHTING_UNIFORMS = 9 * SIZEOF_VEC4 + 8 * SIZEOF_MAT4 + SIZEOF_DIR_LIGHT;
+const U32 SIZEOF_LIGHTING_UNIFORMS =
+	9 * SIZEOF_VEC4 + 8 * SIZEOF_MAT4 + SIZEOF_DIR_LIGHT + SIZEOF_VEC4 * 2u * GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT;
 ANKI_SHADER_STATIC_ASSERT(sizeof(LightingUniforms) == SIZEOF_LIGHTING_UNIFORMS)
 
 ANKI_SHADER_FUNC_INLINE F32 computeClusterKf(ClustererMagicValues magic, Vec3 worldPos)

+ 7 - 4
src/anki/math/Vec.h

@@ -28,11 +28,13 @@ public:
 
 	/// @name Constructors
 	/// @{
+
+	/// Defaut constructor. IT WILL NOT INITIALIZE ANYTHING.
 	TVec()
 	{
 	}
 
-	// Copy
+	/// Copy.
 	TVec(ANKI_ENABLE_ARG(const TVec&, !HAS_VEC4_SIMD) b)
 	{
 		for(U i = 0; i < N; i++)
@@ -41,14 +43,15 @@ public:
 		}
 	}
 
-	// Copy
+	/// Copy.
 	TVec(ANKI_ENABLE_ARG(const TVec&, HAS_VEC4_SIMD) b)
 	{
 		m_simd = b.m_simd;
 	}
 
-	template<typename Y>
-	TVec(const TVec<Y, N>& b)
+	/// Convert from another type.
+	template<typename Y, ANKI_ENABLE(!std::is_same<Y, T>::value)>
+	explicit TVec(const TVec<Y, N>& b)
 	{
 		for(U i = 0; i < N; i++)
 		{

+ 42 - 46
src/anki/renderer/GlobalIllumination.cpp

@@ -45,15 +45,15 @@ public:
 	GlobalIlluminationProbeQueueElement* m_probe ANKI_DEBUG_CODE(= nullptr);
 	UVec3 m_cell ANKI_DEBUG_CODE(= UVec3(MAX_U32));
 
-	Array<RenderTargetDescription, CLIPMAP_LEVEL_COUNT> m_clipmapRtDescriptors;
+	Array<RenderTargetDescription, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT> m_clipmapRtDescriptors;
 
 	Array<RenderTargetHandle, GBUFFER_COLOR_ATTACHMENT_COUNT> m_gbufferColorRts;
 	RenderTargetHandle m_gbufferDepthRt;
 	RenderTargetHandle m_shadowsRt;
 	RenderTargetHandle m_lightShadingRt;
-	Array2d<RenderTargetHandle, CLIPMAP_LEVEL_COUNT, 6> m_clipmapRts;
+	Array2d<RenderTargetHandle, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT, 6> m_clipmapRts;
 
-	Array<Aabb, CLIPMAP_LEVEL_COUNT> m_clipmapLevelAabbs; ///< AABBs in world space.
+	Array<Aabb, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT> m_clipmapLevelAabbs; ///< AABBs in world space.
 	UVec3 m_maxClipmapVolumeSize = UVec3(0u);
 
 	static void foo()
@@ -69,13 +69,19 @@ GlobalIllumination::~GlobalIllumination()
 	m_clipmap.m_grProgs.destroy(getAllocator());
 }
 
-const Array2d<RenderTargetHandle, GlobalIllumination::CLIPMAP_LEVEL_COUNT, 6>&
+const Array2d<RenderTargetHandle, GlobalIllumination::GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT, 6>&
 GlobalIllumination::getClipmapVolumeRenderTargets() const
 {
 	ANKI_ASSERT(m_giCtx);
 	return m_giCtx->m_clipmapRts;
 }
 
+const Aabb& GlobalIllumination::getClipmapAabb(U clipmapLevel) const
+{
+	ANKI_ASSERT(m_giCtx);
+	return m_giCtx->m_clipmapLevelAabbs[clipmapLevel];
+}
+
 Error GlobalIllumination::init(const ConfigSet& cfg)
 {
 	ANKI_R_LOGI("Initializing global illumination");
@@ -221,9 +227,8 @@ Error GlobalIllumination::initClipmap(const ConfigSet& cfg)
 
 	m_clipmap.m_grProgs.create(getAllocator(), m_maxVisibleProbes + 1);
 
-	ShaderProgramResourceConstantValueInitList<3> consts(m_clipmap.m_prog);
+	ShaderProgramResourceConstantValueInitList<2> consts(m_clipmap.m_prog);
 	consts.add("PROBE_COUNT", U32(0));
-	consts.add("CLIPMAP_LEVEL_COUNT", U32(CLIPMAP_LEVEL_COUNT));
 	consts.add("WORKGROUP_SIZE",
 		UVec3(m_clipmap.m_workgroupSize[0], m_clipmap.m_workgroupSize[1], m_clipmap.m_workgroupSize[2]));
 
@@ -241,7 +246,7 @@ Error GlobalIllumination::initClipmap(const ConfigSet& cfg)
 
 	// Init more
 	zeroMemory(m_clipmap.m_volumeSizes);
-	static_assert(CLIPMAP_LEVEL_COUNT == 2, "The following line assume that");
+	static_assert(GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT == 2, "The following line assume that");
 	m_clipmap.m_cellSizes[0] = cfg.getNumber("r.gi.firstClipmapLevelCellSize");
 	m_clipmap.m_cellSizes[1] = cfg.getNumber("r.gi.secondClipmapLevelCellSize");
 	m_clipmap.m_levelMaxDistances[0] = cfg.getNumber("r.gi.firstClipmapMaxDistance");
@@ -261,14 +266,10 @@ void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 
 	// Prepare the probes
 	prepareProbes(rctx, giCtx->m_probe, giCtx->m_cell);
-
-	if(ANKI_LIKELY(giCtx->m_probe == nullptr))
-	{
-		// No probe to update
-		return;
-	}
+	const Bool haveProbeToRender = giCtx->m_probe != nullptr;
 
 	// GBuffer
+	if(haveProbeToRender)
 	{
 		// RTs
 		for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
@@ -298,7 +299,7 @@ void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 	}
 
 	// Shadow pass. Optional
-	if(giCtx->m_probe->m_renderQueues[0]->m_directionalLight.m_uuid
+	if(haveProbeToRender && giCtx->m_probe->m_renderQueues[0]->m_directionalLight.m_uuid
 		&& giCtx->m_probe->m_renderQueues[0]->m_directionalLight.m_shadowCascadeCount > 0)
 	{
 		// Update light matrices
@@ -355,6 +356,7 @@ void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 	}
 
 	// Light shading pass
+	if(haveProbeToRender)
 	{
 		// RT
 		giCtx->m_lightShadingRt = rgraph.newRenderTarget(m_lightShading.m_rtDescr);
@@ -387,6 +389,7 @@ void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 	}
 
 	// Irradiance pass. First & 2nd bounce
+	if(haveProbeToRender)
 	{
 		ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("GI IR");
 
@@ -511,6 +514,7 @@ void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 			entry.m_uuid = probe.m_uuid;
 			entry.m_probeAabbMin = probe.m_aabbMin;
 			entry.m_probeAabbMax = probe.m_aabbMax;
+			entry.m_volumeSize = probe.m_cellCounts;
 			m_probeUuidToCacheEntryIdx.emplace(getAllocator(), probe.m_uuid, cacheEntryIdx);
 		}
 
@@ -543,13 +547,13 @@ void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 		// Compute the render position
 		const U cellToRender = entry.m_renderedCells++;
 		ANKI_ASSERT(cellToRender < probe.m_totalCellCount);
-		unflatten3dArrayIndex(probe.m_cellCounts.x(),
+		unflatten3dArrayIndex(probe.m_cellCounts.z(),
 			probe.m_cellCounts.y(),
-			probe.m_cellCounts.z(),
+			probe.m_cellCounts.x(),
 			cellToRender,
-			probeToUpdateThisFrameCell.x(),
+			probeToUpdateThisFrameCell.z(),
 			probeToUpdateThisFrameCell.y(),
-			probeToUpdateThisFrameCell.z());
+			probeToUpdateThisFrameCell.x());
 
 		// Inform probe about its next frame
 		if(entry.m_renderedCells == probe.m_totalCellCount)
@@ -767,13 +771,13 @@ void GlobalIllumination::runClipmapPopulation(RenderPassWorkContext& rgraphCtx,
 
 	struct Unis
 	{
-		Clipmap m_clipmaps[CLIPMAP_LEVEL_COUNT];
+		Clipmap m_clipmaps[GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT];
 		Vec3 m_cameraPos;
 		U32 m_padding;
 	};
 	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 0);
 
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
 		unis->m_clipmaps[level].m_aabbMin = giCtx.m_clipmapLevelAabbs[level].getMin().xyz();
 		unis->m_clipmaps[level].m_cellSize = m_clipmap.m_cellSizes[level];
@@ -785,7 +789,7 @@ void GlobalIllumination::runClipmapPopulation(RenderPassWorkContext& rgraphCtx,
 	unis->m_cameraPos = giCtx.m_ctx->m_renderQueue->m_cameraTransform.getTranslationPart().xyz();
 
 	// Bind out images
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
 		for(U dir = 0; dir < 6; ++dir)
 		{
@@ -845,13 +849,12 @@ void GlobalIllumination::populateRenderGraphClipmap(InternalContext& giCtx)
 	const RenderQueue& rqueue = *giCtx.m_ctx->m_renderQueue;
 
 	// Compute the size of the clipmap levels
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
-		UVec3 clipmapVolumeSize;
-
 		// Get the edges of the sub frustum in local space
 		const F32 near = (level == 0) ? EPSILON : m_clipmap.m_levelMaxDistances[level - 1];
-		const F32 far = (level != CLIPMAP_LEVEL_COUNT - 1) ? m_clipmap.m_levelMaxDistances[level] : rqueue.m_cameraFar;
+		const F32 far = (level != GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT - 1) ? m_clipmap.m_levelMaxDistances[level]
+																			   : rqueue.m_cameraFar;
 		Array<Vec4, 8> frustumEdges;
 		computeEdgesOfFrustum(near, rqueue.m_cameraFovX, rqueue.m_cameraFovY, &frustumEdges[0]);
 		computeEdgesOfFrustum(far, rqueue.m_cameraFovX, rqueue.m_cameraFovY, &frustumEdges[4]);
@@ -869,36 +872,29 @@ void GlobalIllumination::populateRenderGraphClipmap(InternalContext& giCtx)
 			sizeof(frustumEdges));
 
 		// Align the AABB to the cell grid
-		Vec4 min(0.0f), max(0.0f);
+		Vec3 minv(0.0f), maxv(0.0f);
 		for(U comp = 0; comp < 3; ++comp)
 		{
-			min[comp] =
+			minv[comp] =
 				getAlignedRoundDown(m_clipmap.m_cellSizes[level], giCtx.m_clipmapLevelAabbs[level].getMin()[comp]);
-
-			max[comp] =
+			maxv[comp] =
 				getAlignedRoundUp(m_clipmap.m_cellSizes[level], giCtx.m_clipmapLevelAabbs[level].getMax()[comp]);
 		}
 
-		giCtx.m_clipmapLevelAabbs[level].setMin(min);
-		giCtx.m_clipmapLevelAabbs[level].setMax(max);
-
-		// Compute the volume sizes
-		for(U comp = 0; comp < 3; ++comp)
-		{
-			const Vec4& min = giCtx.m_clipmapLevelAabbs[level].getMin();
-			const Vec4& max = giCtx.m_clipmapLevelAabbs[level].getMax();
-			clipmapVolumeSize[comp] = U32((max[comp] - min[comp]) / m_clipmap.m_cellSizes[level]);
-			ANKI_ASSERT(clipmapVolumeSize[comp] >= m_clipmap.m_cellSizes[level]);
-		}
+		// Maximize the size of the volumes to avoid creating new RTs every frame
+		const Vec3 volumeSize = (maxv - minv) / m_clipmap.m_cellSizes[level];
+		m_clipmap.m_volumeSizes[level] = m_clipmap.m_volumeSizes[level].max(UVec3(volumeSize));
 
-		giCtx.m_maxClipmapVolumeSize = giCtx.m_maxClipmapVolumeSize.max(clipmapVolumeSize);
+		maxv = minv + Vec3(m_clipmap.m_volumeSizes[level]) * m_clipmap.m_cellSizes[level];
+		giCtx.m_clipmapLevelAabbs[level].setMin(minv);
+		giCtx.m_clipmapLevelAabbs[level].setMax(maxv);
 
-		// Maximize the size of the volumes to avoid creating new RTs every frame
-		m_clipmap.m_volumeSizes[level] = m_clipmap.m_volumeSizes[level].max(clipmapVolumeSize);
+		// Compute the max volume size for all levels
+		giCtx.m_maxClipmapVolumeSize = giCtx.m_maxClipmapVolumeSize.max(m_clipmap.m_volumeSizes[level]);
 	}
 
 	// Create a few RT descriptors
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
 		RenderTargetDescription& descr = giCtx.m_clipmapRtDescriptors[level];
 
@@ -912,7 +908,7 @@ void GlobalIllumination::populateRenderGraphClipmap(InternalContext& giCtx)
 	}
 
 	// Ask for render targets
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
 		for(U dir = 0; dir < 6; ++dir)
 		{
@@ -940,7 +936,7 @@ void GlobalIllumination::populateRenderGraphClipmap(InternalContext& giCtx)
 		}
 	}
 
-	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
 	{
 		for(U i = 0; i < 6; ++i)
 		{

+ 9 - 9
src/anki/renderer/GlobalIllumination.h

@@ -8,6 +8,7 @@
 #include <anki/renderer/RendererObject.h>
 #include <anki/renderer/TraditionalDeferredShading.h>
 #include <anki/renderer/RenderQueue.h>
+#include <anki/collision/Forward.h>
 
 namespace anki
 {
@@ -21,7 +22,7 @@ namespace anki
 class GlobalIllumination : public RendererObject
 {
 private:
-	static constexpr U CLIPMAP_LEVEL_COUNT = 2;
+	static constexpr U GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT = 2;
 
 anki_internal:
 	GlobalIllumination(Renderer* r)
@@ -38,12 +39,11 @@ anki_internal:
 	void populateRenderGraph(RenderingContext& ctx);
 
 	/// Return a number of volume render targets.
-	const Array2d<RenderTargetHandle, CLIPMAP_LEVEL_COUNT, 6>& getClipmapVolumeRenderTargets() const;
+	const Array2d<RenderTargetHandle, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT, 6>&
+	getClipmapVolumeRenderTargets() const;
 
-	static constexpr U getClipmapLevelCount()
-	{
-		return CLIPMAP_LEVEL_COUNT;
-	}
+	/// Return the clipmap AABB.
+	const Aabb& getClipmapAabb(U clipmapLevel) const;
 
 private:
 	class InternalContext;
@@ -102,10 +102,10 @@ private:
 	public:
 		ShaderProgramResourcePtr m_prog;
 		DynamicArray<ShaderProgramPtr> m_grProgs; ///< One program for a number of probe counts.
-		Array<UVec3, CLIPMAP_LEVEL_COUNT> m_volumeSizes; ///< This is dynamic.
-		Array<F32, CLIPMAP_LEVEL_COUNT> m_cellSizes;
+		Array<UVec3, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT> m_volumeSizes; ///< This is dynamic.
+		Array<F32, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT> m_cellSizes;
 		Array<U8, 3> m_workgroupSize = {{8, 8, 8}};
-		Array<F32, CLIPMAP_LEVEL_COUNT - 1> m_levelMaxDistances;
+		Array<F32, GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT - 1> m_levelMaxDistances;
 	} m_clipmap; ///< Clipmap population.
 
 	InternalContext* m_giCtx = nullptr;

+ 27 - 8
src/anki/renderer/LightShading.cpp

@@ -14,6 +14,7 @@
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Ssr.h>
+#include <anki/renderer/GlobalIllumination.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 
@@ -122,15 +123,24 @@ void LightShading::run(RenderPassWorkContext& rgraphCtx)
 		bindStorage(cmdb, 0, 8, rsrc.m_clustersToken);
 		bindStorage(cmdb, 0, 9, rsrc.m_indicesToken);
 
-		cmdb->bindSampler(0, 10, m_r->getSamplers().m_nearestNearestClamp);
-		cmdb->bindSampler(0, 11, m_r->getSamplers().m_trilinearRepeat);
-		rgraphCtx.bindColorTexture(0, 12, m_r->getGBuffer().getColorRt(0));
-		rgraphCtx.bindColorTexture(0, 13, m_r->getGBuffer().getColorRt(1));
-		rgraphCtx.bindColorTexture(0, 14, m_r->getGBuffer().getColorRt(2));
+		const auto& arr = m_r->getGlobalIllumination().getClipmapVolumeRenderTargets();
+		for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
+		{
+			for(U dir = 0; dir < 6; ++dir)
+			{
+				rgraphCtx.bindColorTexture(0, 10, arr[level][dir], level * 6 + dir);
+			}
+		}
+
+		cmdb->bindSampler(0, 11, m_r->getSamplers().m_nearestNearestClamp);
+		cmdb->bindSampler(0, 12, m_r->getSamplers().m_trilinearRepeat);
+		rgraphCtx.bindColorTexture(0, 13, m_r->getGBuffer().getColorRt(0));
+		rgraphCtx.bindColorTexture(0, 14, m_r->getGBuffer().getColorRt(1));
+		rgraphCtx.bindColorTexture(0, 15, m_r->getGBuffer().getColorRt(2));
 		rgraphCtx.bindTexture(
-			0, 15, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
-		rgraphCtx.bindColorTexture(0, 16, m_r->getSsr().getRt());
-		rgraphCtx.bindColorTexture(0, 17, m_r->getSsao().getRt());
+			0, 16, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
+		rgraphCtx.bindColorTexture(0, 17, m_r->getSsr().getRt());
+		rgraphCtx.bindColorTexture(0, 18, m_r->getSsao().getRt());
 
 		// Draw
 		drawQuad(cmdb);
@@ -205,6 +215,15 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newDependency({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newDependency({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 
+	const auto& arr = m_r->getGlobalIllumination().getClipmapVolumeRenderTargets();
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		for(U dir = 0; dir < 6; ++dir)
+		{
+			pass.newDependency({arr[level][dir], TextureUsageBit::SAMPLED_FRAGMENT});
+		}
+	}
+
 	// Fog
 	pass.newDependency({m_r->getVolumetricFog().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 

+ 9 - 0
src/anki/renderer/Renderer.cpp

@@ -8,6 +8,7 @@
 #include <anki/core/Trace.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
+#include <anki/collision/Aabb.h>
 
 #include <anki/renderer/Indirect.h>
 #include <anki/renderer/GBuffer.h>
@@ -599,6 +600,14 @@ void Renderer::updateLightShadingUniforms(RenderingContext& ctx) const
 	{
 		blk->m_dirLight.m_active = 0;
 	}
+
+	// GI clipmap
+	for(U level = 0; level < GLOBAL_ILLUMINATION_CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		const Aabb& box = m_gi->getClipmapAabb(level);
+		blk->m_globalIlluminationClipmapLevels[level].m_min = box.getMin().xyz();
+		blk->m_globalIlluminationClipmapLevels[level].m_max = box.getMax().xyz();
+	}
 }
 
 } // end namespace anki

+ 5 - 0
src/anki/renderer/Renderer.h

@@ -181,6 +181,11 @@ public:
 		return *m_lensFlare;
 	}
 
+	const GlobalIllumination& getGlobalIllumination() const
+	{
+		return *m_gi;
+	}
+
 	UiStage& getUiStage()
 	{
 		return *m_uiStage;

+ 1 - 1
src/anki/scene/components/GlobalIlluminationProbeComponent.h

@@ -105,7 +105,7 @@ private:
 	Vec3 m_aabbMin = Vec3(-1.0f);
 	Vec3 m_aabbMax = Vec3(+1.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
-	F32 m_cellSize = 2.0f; ///< Cell size in meters.
+	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	UVec3 m_cellCounts = UVec3(2u);
 	Bool m_markedForRendering = false;
 	Bool m_dirty = true;