ソースを参照

Move volumetric lighting to the new GI

Panagiotis Christopoulos Charitos 6 年 前
コミット
e578aa553b

+ 7 - 7
shaders/ClusteredShadingCommon.glsl

@@ -60,15 +60,15 @@ layout(set = LIGHT_SET, binding = LIGHT_LIGHTS_BINDING + 2) uniform highp textur
 //
 // Indirect uniforms (4)
 //
-#if defined(LIGHT_INDIRECT_BINDING)
-layout(std140, row_major, set = LIGHT_SET, binding = LIGHT_INDIRECT_BINDING) uniform u3_
+#if defined(LIGHT_INDIRECT_SPECULAR_BINDING)
+layout(std140, row_major, set = LIGHT_SET, binding = LIGHT_INDIRECT_SPECULAR_BINDING) uniform u3_
 {
 	ReflectionProbe u_reflectionProbes[UBO_MAX_SIZE / SIZEOF_REFLECTION_PROBE];
 };
 
-layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_BINDING + 1) uniform textureCubeArray u_reflectionsTex;
-layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_BINDING + 2) uniform textureCubeArray u_irradianceTex;
-layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_BINDING + 3) uniform texture2D u_integrationLut;
+layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_SPECULAR_BINDING + 1) uniform textureCubeArray u_reflectionsTex;
+layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_SPECULAR_BINDING + 2) uniform textureCubeArray u_irradianceTex;
+layout(set = LIGHT_SET, binding = LIGHT_INDIRECT_SPECULAR_BINDING + 3) uniform texture2D u_integrationLut;
 #endif
 
 //
@@ -85,7 +85,7 @@ layout(set = LIGHT_SET, binding = LIGHT_DECALS_BINDING + 2) uniform texture2D u_
 #endif
 
 //
-// Fog density uniforms
+// Fog density uniforms (1)
 //
 #if defined(LIGHT_FOG_DENSITY_VOLUMES_BINDING)
 layout(std140, row_major, set = LIGHT_SET, binding = LIGHT_FOG_DENSITY_VOLUMES_BINDING) uniform u5_
@@ -155,4 +155,4 @@ Vec3 lightHeatmap(U32 firstIndex, U32 maxLights, Bool decals, Bool plights, Bool
 
 	const F32 factor = min(1.0, F32(count) / F32(maxLights));
 	return heatmap(factor);
-}
+}

+ 1 - 1
shaders/LightShading.glslp

@@ -37,7 +37,7 @@ void main()
 #define LIGHT_SET 0
 #define LIGHT_COMMON_UNIS_BINDING 0
 #define LIGHT_LIGHTS_BINDING 1
-#define LIGHT_INDIRECT_BINDING 4
+#define LIGHT_INDIRECT_SPECULAR_BINDING 4
 #define LIGHT_GLOBAL_ILLUMINATION_BINDING 8
 #define LIGHT_CLUSTERS_BINDING 10
 #include <shaders/ClusteredShadingCommon.glsl>

+ 67 - 39
shaders/VolumetricLightingAccumulation.glslp

@@ -39,9 +39,9 @@ layout(push_constant, std430) uniform pc_
 #define LIGHT_SET 0
 #define LIGHT_COMMON_UNIS_BINDING 5
 #define LIGHT_LIGHTS_BINDING 6
-#define LIGHT_INDIRECT_BINDING 9
-#define LIGHT_FOG_DENSITY_VOLUMES_BINDING 13
-#define LIGHT_CLUSTERS_BINDING 14
+#define LIGHT_GLOBAL_ILLUMINATION_BINDING 9
+#define LIGHT_FOG_DENSITY_VOLUMES_BINDING 11
+#define LIGHT_CLUSTERS_BINDING 12
 #include <shaders/ClusteredShadingCommon.glsl>
 
 Vec3 g_globalInvocationID = Vec3(gl_GlobalInvocationID);
@@ -169,51 +169,79 @@ Vec4 accumulateLightsAndFog(U32 clusterIdx, Vec3 worldPos, F32 linearDepth)
 		color += light.m_diffuseColor * factor;
 	}
 
-	// Probes
-	F32 totalBlendWeight = EPSILON;
-	Vec3 diffIndirect = Vec3(0.0);
-	ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+	// Indirect diffuse GI
 	{
-		const ReflectionProbe probe = u_reflectionProbes[idx];
-		const Vec3 aabbMin = probe.m_aabbMin;
-		const Vec3 aabbMax = probe.m_aabbMax;
-		const Vec3 probeOrigin = probe.m_position;
-		const F32 cubemapIndex = probe.m_cubemapIndex;
-
-		const F32 blendWeight = computeProbeBlendWeight(worldPos, aabbMin, aabbMax, 0.2);
-		totalBlendWeight += blendWeight;
-
-		Vec3 c = textureLod(u_irradianceTex, u_linearAnyClampSampler, Vec4(viewDir, cubemapIndex), 0.0).rgb;
-		c *= PI; // Irradiance is pre-divided with PI so fix it
-		diffIndirect += c * blendWeight;
-	}
+		idxOffset = u_clusters[clusterIdx];
+		idxOffset = u_lightIndices[idxOffset - 3u];
+		Vec3 diffIndirect;
 
-	diffIndirect /= totalBlendWeight;
-	color += diffIndirect;
+		if(subgroupAll(u_lightIndices[idxOffset] != MAX_U32 && u_lightIndices[idxOffset + 1u] == MAX_U32))
+		{
+			// Only one probe, do a fast path without blend weight
 
-	// Fog density
-	F32 fogDensity = 0.0;
-	idxOffset = u_clusters[clusterIdx];
-	idxOffset = u_lightIndices[idxOffset - 1u];
-	ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
-	{
-		const FogDensityVolume vol = u_fogDensityVolumes[idx];
+			GlobalIlluminationProbe probe = u_giProbes[u_lightIndices[idxOffset]];
 
-		F32 factor;
-		ANKI_BRANCH if(vol.m_isBox == 1u)
-		{
-			factor =
-				computeProbeBlendWeight(worldPos, vol.m_aabbMinOrSphereCenter, vol.m_aabbMaxOrSphereRadiusSquared, 0.2);
+			// Sample
+			diffIndirect = sampleGlobalIllumination(
+				worldPos, viewDir, probe, u_globalIlluminationTextures, u_linearAnyClampSampler);
 		}
 		else
 		{
-			const Vec3 diff = worldPos - vol.m_aabbMinOrSphereCenter;
-			F32 distSq = dot(diff, diff) / vol.m_aabbMaxOrSphereRadiusSquared.x;
-			distSq = min(1.0, distSq);
-			factor = 1.0 - distSq;
+			// Zero or more than one probes, do a slow path that blends them together
+
+			F32 totalBlendWeight = EPSILON;
+			diffIndirect = Vec3(0.0);
+
+			// Loop probes
+			ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+			{
+				GlobalIlluminationProbe probe = u_giProbes[idx];
+
+				// Compute blend weight
+				const F32 blendWeight =
+					computeProbeBlendWeight(worldPos, probe.m_aabbMin, probe.m_aabbMax, probe.m_fadeDistance);
+				totalBlendWeight += blendWeight;
+
+				// Sample
+				const Vec3 c = sampleGlobalIllumination(
+					worldPos, viewDir, probe, u_globalIlluminationTextures, u_linearAnyClampSampler);
+				diffIndirect += c * blendWeight;
+			}
+
+			// Normalize
+			diffIndirect /= totalBlendWeight;
 		}
 
-		fogDensity += vol.m_density * factor;
+		diffIndirect *= PI; // Irradiance is pre-divided with PI so fix it
+
+		color += diffIndirect;
+	}
+
+	// Fog density
+	F32 fogDensity = 0.0;
+	{
+		idxOffset = u_clusters[clusterIdx];
+		idxOffset = u_lightIndices[idxOffset - 1u];
+		ANKI_LOOP while((idx = u_lightIndices[idxOffset++]) != MAX_U32)
+		{
+			const FogDensityVolume vol = u_fogDensityVolumes[idx];
+
+			F32 factor;
+			ANKI_BRANCH if(vol.m_isBox == 1u)
+			{
+				factor = computeProbeBlendWeight(
+					worldPos, vol.m_aabbMinOrSphereCenter, vol.m_aabbMaxOrSphereRadiusSquared, 0.2);
+			}
+			else
+			{
+				const Vec3 diff = worldPos - vol.m_aabbMinOrSphereCenter;
+				F32 distSq = dot(diff, diff) / vol.m_aabbMaxOrSphereRadiusSquared.x;
+				distSq = min(1.0, distSq);
+				factor = 1.0 - distSq;
+			}
+
+			fogDensity += vol.m_density * factor;
+		}
 	}
 
 	return Vec4(color, fogDensity);

+ 5 - 3
src/anki/renderer/ClusterBin.cpp

@@ -724,8 +724,10 @@ void ClusterBin::writeTypedObjectsToGpuBuffers(BinCtx& ctx) const
 	const U visibleProbeCount = rqueue.m_reflectionProbes.getSize();
 	if(visibleProbeCount)
 	{
-		ReflectionProbe* data = static_cast<ReflectionProbe*>(ctx.m_in->m_stagingMem->allocateFrame(
-			sizeof(ReflectionProbe) * visibleProbeCount, StagingGpuMemoryType::UNIFORM, ctx.m_out->m_probesToken));
+		ReflectionProbe* data = static_cast<ReflectionProbe*>(
+			ctx.m_in->m_stagingMem->allocateFrame(sizeof(ReflectionProbe) * visibleProbeCount,
+				StagingGpuMemoryType::UNIFORM,
+				ctx.m_out->m_reflectionProbesToken));
 
 		WeakArray<ReflectionProbe> gpuProbes(data, visibleProbeCount);
 
@@ -742,7 +744,7 @@ void ClusterBin::writeTypedObjectsToGpuBuffers(BinCtx& ctx) const
 	}
 	else
 	{
-		ctx.m_out->m_probesToken.markUnused();
+		ctx.m_out->m_reflectionProbesToken.markUnused();
 	}
 
 	// Fog volumes

+ 2 - 2
src/anki/renderer/ClusterBin.h

@@ -38,7 +38,7 @@ class ClusterBinOut
 public:
 	StagingGpuMemoryToken m_pointLightsToken;
 	StagingGpuMemoryToken m_spotLightsToken;
-	StagingGpuMemoryToken m_probesToken;
+	StagingGpuMemoryToken m_reflectionProbesToken;
 	StagingGpuMemoryToken m_decalsToken;
 	StagingGpuMemoryToken m_fogDensityVolumesToken;
 	StagingGpuMemoryToken m_globalIlluminationProbesToken;
@@ -83,4 +83,4 @@ private:
 };
 /// @}
 
-} // end namespace anki
+} // end namespace anki

+ 26 - 0
src/anki/renderer/GlobalIllumination.cpp

@@ -73,6 +73,32 @@ const RenderTargetHandle& GlobalIllumination::getVolumeRenderTarget(
 	return m_giCtx->m_irradianceProbeRts[idx];
 }
 
+void GlobalIllumination::setRenderGraphDependencies(
+	RenderingContext& ctx, RenderPassDescriptionBase& pass, TextureUsageBit usage) const
+{
+	for(U idx = 0; idx < ctx.m_renderQueue->m_giProbes.getSize(); ++idx)
+	{
+		pass.newDependency({getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]), usage});
+	}
+}
+
+void GlobalIllumination::bindVolumeTextures(
+	const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx, U32 set, U32 binding) const
+{
+	for(U idx = 0; idx < MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES; ++idx)
+	{
+		if(idx < ctx.m_renderQueue->m_giProbes.getSize())
+		{
+			rgraphCtx.bindColorTexture(set, binding, getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]), idx);
+		}
+		else
+		{
+			rgraphCtx.m_commandBuffer->bindTexture(
+				set, binding, m_r->getDummyTextureView3d(), TextureUsageBit::SAMPLED_ALL, idx);
+		}
+	}
+}
+
 Error GlobalIllumination::init(const ConfigSet& cfg)
 {
 	ANKI_R_LOGI("Initializing global illumination");

+ 7 - 0
src/anki/renderer/GlobalIllumination.h

@@ -38,6 +38,13 @@ anki_internal:
 	/// Return the volume RT given a cache entry index.
 	const RenderTargetHandle& getVolumeRenderTarget(const GlobalIlluminationProbeQueueElement& probe) const;
 
+	/// Set the render graph dependencies.
+	void setRenderGraphDependencies(
+		RenderingContext& ctx, RenderPassDescriptionBase& pass, TextureUsageBit usage) const;
+
+	/// Bind the volume textures to a command buffer.
+	void bindVolumeTextures(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx, U32 set, U32 binding) const;
+
 private:
 	class InternalContext;
 

+ 3 - 18
src/anki/renderer/LightShading.cpp

@@ -115,23 +115,12 @@ void LightShading::run(RenderPassWorkContext& rgraphCtx)
 		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
 		rgraphCtx.bindColorTexture(0, 3, m_r->getShadowMapping().getShadowmapRt());
 
-		bindUniforms(cmdb, 0, 4, rsrc.m_probesToken);
+		bindUniforms(cmdb, 0, 4, rsrc.m_reflectionProbesToken);
 		rgraphCtx.bindColorTexture(0, 5, m_r->getIndirect().getReflectionRt());
 		rgraphCtx.bindColorTexture(0, 6, m_r->getIndirect().getIrradianceRt());
 		cmdb->bindTexture(0, 7, m_r->getIndirect().getIntegrationLut(), TextureUsageBit::SAMPLED_FRAGMENT);
 
-		for(U idx = 0; idx < MAX_VISIBLE_GLOBAL_ILLUMINATION_PROBES; ++idx)
-		{
-			if(idx < ctx.m_renderQueue->m_giProbes.getSize())
-			{
-				rgraphCtx.bindColorTexture(
-					0, 8, m_r->getGlobalIllumination().getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]), idx);
-			}
-			else
-			{
-				cmdb->bindTexture(0, 8, m_r->getDummyTextureView3d(), TextureUsageBit::SAMPLED_FRAGMENT, idx);
-			}
-		}
+		m_r->getGlobalIllumination().bindVolumeTextures(ctx, rgraphCtx, 0, 8);
 		bindUniforms(cmdb, 0, 9, rsrc.m_globalIlluminationProbesToken);
 
 		bindStorage(cmdb, 0, 10, rsrc.m_clustersToken);
@@ -220,11 +209,7 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newDependency({m_r->getIndirect().getReflectionRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newDependency({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 
-	for(U idx = 0; idx < ctx.m_renderQueue->m_giProbes.getSize(); ++idx)
-	{
-		pass.newDependency({m_r->getGlobalIllumination().getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]),
-			TextureUsageBit::SAMPLED_FRAGMENT});
-	}
+	m_r->getGlobalIllumination().setRenderGraphDependencies(ctx, pass, TextureUsageBit::SAMPLED_FRAGMENT);
 
 	// Fog
 	pass.newDependency({m_r->getVolumetricFog().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});

+ 17 - 5
src/anki/renderer/RenderQueue.h

@@ -151,14 +151,14 @@ using ReflectionProbeQueueElementFeedbackCallback = void (*)(Bool fillRenderQueu
 class ReflectionProbeQueueElement final
 {
 public:
+	U64 m_uuid;
 	ReflectionProbeQueueElementFeedbackCallback m_feedbackCallback;
 	RenderQueueDrawCallback m_drawCallback;
 	void* m_userData;
-	U64 m_uuid;
+	Array<RenderQueue*, 6> m_renderQueues;
 	Vec3 m_worldPosition;
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMax;
-	Array<RenderQueue*, 6> m_renderQueues;
 	U32 m_textureArrayIndex; ///< Renderer internal.
 
 	ReflectionProbeQueueElement()
@@ -192,6 +192,18 @@ public:
 	GlobalIlluminationProbeQueueElement()
 	{
 	}
+
+	Bool operator<(const GlobalIlluminationProbeQueueElement& b) const
+	{
+		if(m_cellSizes.x() != b.m_cellSizes.x())
+		{
+			return m_cellSizes.x() < b.m_cellSizes.x();
+		}
+		else
+		{
+			return m_totalCellCount < b.m_totalCellCount;
+		}
+	}
 };
 
 static_assert(std::is_trivially_destructible<GlobalIlluminationProbeQueueElement>::value == true,
@@ -201,13 +213,13 @@ static_assert(std::is_trivially_destructible<GlobalIlluminationProbeQueueElement
 class LensFlareQueueElement final
 {
 public:
-	Vec3 m_worldPosition;
-	Vec2 m_firstFlareSize;
-	Vec4 m_colorMultiplier;
 	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
 	const TextureView* m_textureView;
 	const void* m_userData;
 	RenderQueueDrawCallback m_drawCallback;
+	Vec3 m_worldPosition;
+	Vec2 m_firstFlareSize;
+	Vec4 m_colorMultiplier;
 
 	LensFlareQueueElement()
 	{

+ 8 - 9
src/anki/renderer/VolumetricLightingAccumulation.cpp

@@ -5,7 +5,7 @@
 
 #include <anki/renderer/VolumetricLightingAccumulation.h>
 #include <anki/renderer/ShadowMapping.h>
-#include <anki/renderer/Indirect.h>
+#include <anki/renderer/GlobalIllumination.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/misc/ConfigSet.h>
@@ -96,7 +96,8 @@ void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 	pass.newDependency({m_runCtx.m_rts[0], TextureUsageBit::SAMPLED_COMPUTE});
 	pass.newDependency({m_runCtx.m_rts[1], TextureUsageBit::IMAGE_COMPUTE_WRITE});
 	pass.newDependency({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_COMPUTE});
-	pass.newDependency({m_r->getIndirect().getIrradianceRt(), TextureUsageBit::SAMPLED_COMPUTE});
+
+	m_r->getGlobalIllumination().setRenderGraphDependencies(ctx, pass, TextureUsageBit::SAMPLED_COMPUTE);
 }
 
 void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
@@ -122,14 +123,12 @@ void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
 	bindUniforms(cmdb, 0, 7, rsrc.m_spotLightsToken);
 	rgraphCtx.bindColorTexture(0, 8, m_r->getShadowMapping().getShadowmapRt());
 
-	bindUniforms(cmdb, 0, 9, rsrc.m_probesToken);
-	cmdb->bindTexture(0, 10, m_r->getDummyTextureView2d(), TextureUsageBit::SAMPLED_COMPUTE);
-	rgraphCtx.bindColorTexture(0, 11, m_r->getIndirect().getIrradianceRt());
-	cmdb->bindTexture(0, 12, m_r->getDummyTextureView2d(), TextureUsageBit::SAMPLED_COMPUTE);
+	m_r->getGlobalIllumination().bindVolumeTextures(ctx, rgraphCtx, 0, 9);
+	bindUniforms(cmdb, 0, 10, rsrc.m_globalIlluminationProbesToken);
 
-	bindUniforms(cmdb, 0, 13, rsrc.m_fogDensityVolumesToken);
-	bindStorage(cmdb, 0, 14, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 15, rsrc.m_indicesToken);
+	bindUniforms(cmdb, 0, 11, rsrc.m_fogDensityVolumesToken);
+	bindStorage(cmdb, 0, 12, rsrc.m_clustersToken);
+	bindStorage(cmdb, 0, 13, rsrc.m_indicesToken);
 
 	struct PushConsts
 	{

+ 2 - 0
src/anki/scene/Visibility.cpp

@@ -700,6 +700,8 @@ void CombineResultsTask::combine()
 		results.m_forwardShadingRenderables.getEnd(),
 		RevDistanceSortFunctor<RenderableQueueElement>());
 
+	std::sort(results.m_giProbes.getBegin(), results.m_giProbes.getEnd());
+
 	// Cleanup
 	if(m_frcCtx->m_r)
 	{