Panagiotis Christopoulos Charitos пре 7 година
родитељ
комит
72c1ddf265

BIN
engine_data/BlueNoise_Rgb8_64x64x64_3D.ankitex


+ 39 - 23
shaders/VolumetricLightingAccumulation.glslp

@@ -9,6 +9,7 @@
 
 #pragma anki input const UVec3 VOLUME_SIZE
 #pragma anki input const UVec3 CLUSTER_COUNT
+#pragma anki input const U32 FINAL_CLUSTER_Z
 #pragma anki input const UVec3 WORKGROUP_SIZE
 #pragma anki input const UVec3 NOISE_TEX_SIZE
 
@@ -19,12 +20,13 @@ layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_s
 layout(ANKI_IMAGE_BINDING(0, 0)) writeonly uniform image3D out_volume;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler3D u_noiseTex;
 
-layout(ANKI_UBO_BINDING(0, 0)) uniform ub0_
+struct PushConsts
 {
-	Vec4 u_noiseOffsetPad3;
+	Vec4 m_noiseOffsetPad3;
 };
+ANKI_PUSH_CONSTANTS(PushConsts, u_regs);
 
-#define u_noiseOffset u_noiseOffsetPad3.x
+#define u_noiseOffset u_regs.m_noiseOffsetPad3.x
 
 #define LIGHT_TEX_BINDING 1
 #define LIGHT_UBO_BINDING 0
@@ -34,31 +36,42 @@ layout(ANKI_UBO_BINDING(0, 0)) uniform ub0_
 #define LIGHT_COMMON_UNIS
 #include <shaders/ClusteredShadingCommon.glsl>
 
+const UVec3 FRACTION = UVec3(VOLUME_SIZE.xy / CLUSTER_COUNT.xy, VOLUME_SIZE.z / (FINAL_CLUSTER_Z + 1));
+Vec3 g_globalInvocationID = Vec3(gl_GlobalInvocationID);
+
 Vec3 readRand()
 {
-	Vec3 uv = (Vec3(gl_GlobalInvocationID) + 0.5) / Vec3(NOISE_MAP_SIZE);
+	Vec3 uv = (g_globalInvocationID + 0.5) / Vec3(NOISE_TEX_SIZE);
 	uv.z += u_noiseOffset;
-
 	return textureLod(u_noiseTex, uv, 0.0).rgb;
 }
 
-Vec3 randWPos(U32 clusterK)
+Vec3 randWPos()
 {
 	// Read a rand value
 	Vec3 rand = readRand();
 
-	F32 zVSpaceNear = computeClusterNear(u_clusterMagic, clusterXYZ.z);
-	F32 zVSpaceFar = computeClusterFar(u_clusterMagic, clusterXYZ.z);
-	F32 zVSpace = mix(zVSpaceNear, zVSpaceFar, rand.z);
+	// Compute the cluster Z as float
+	F32 clusterK = g_globalInvocationID.z * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
+	F32 clusterK_1 = (g_globalInvocationID.z + 1.0) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
+
+	// Get a Z value
+	F32 zVSpaceNear = computeClusterNearf(u_clustererMagic, clusterK);
+	F32 zVSpaceFar = computeClusterNearf(u_clustererMagic, clusterK_1);
+	F32 zVSpace = -mix(zVSpaceNear, zVSpaceFar, rand.z);
 
-	Vec2 uvMin = Vec2(gl_GlobalInvocationID.xy) / Vec2(VOLUME_SIZE.xy);
+	// Get a XY value
+	Vec2 uvMin = g_globalInvocationID.xy / Vec2(VOLUME_SIZE.xy);
 	Vec2 uvMax = uvMin + 1.0 / Vec2(VOLUME_SIZE.xy);
 	Vec2 uv = mix(uvMin, uvMax, rand.xy);
 	Vec2 ndc = UV_TO_NDC(uv);
-	Vec3 xyZVspace = ndc * u_unprojectionParams.xy * zVSpace;
+	Vec2 xyZVspace = ndc * u_unprojectionParams.xy * zVSpace;
 
-	Vec4 worldPos4 = u_invViewMat * Vec4(ndc, depth, 1.0);
-	Vec3 worldPos = worldPos4.xyz / worldPos4.w;
+	// Get the pos
+	Vec4 worldPos4 = u_invViewMat * Vec4(xyZVspace, zVSpace, 1.0);
+	Vec3 worldPos = worldPos4.xyz;
+
+	return worldPos;
 }
 
 Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
@@ -66,7 +79,7 @@ Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
 	Vec3 color = Vec3(0.0);
 
 	// Get ID offset
-	U32 lightIdxOffset = u_clusters[clusterIdx];
+	U32 idxOffset = u_clusters[clusterIdx];
 
 	// Skip decals
 	U32 count = u_lightIndices[idxOffset];
@@ -79,7 +92,7 @@ Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
 	{
 		PointLight light = u_pointLights[u_lightIndices[idxOffset++]];
 
-		Vec3 frag2Light = light.m_posRadius.xyz - fragPos;
+		Vec3 frag2Light = light.m_posRadius.xyz - worldPos;
 		F32 factor = computeAttenuationFactor(light.m_posRadius.w, frag2Light);
 
 #if ENABLE_SHADOWS
@@ -100,7 +113,7 @@ Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
 	{
 		SpotLight light = u_spotLights[u_lightIndices[idxOffset++]];
 
-		Vec3 frag2Light = light.m_posRadius.xyz - fragPos;
+		Vec3 frag2Light = light.m_posRadius.xyz - worldPos;
 		F32 factor = computeAttenuationFactor(light.m_posRadius.w, frag2Light);
 
 		Vec3 l = normalize(frag2Light);
@@ -112,7 +125,8 @@ Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
 		F32 shadowmapLayerIdx = light.m_diffuseColorShadowmapId.w;
 		if(shadowmapLayerIdx >= 0.0)
 		{
-			factor *= computeShadowFactorSpot(light.m_texProjectionMat, fragPos, light.m_lightDirRadius.w, u_shadowTex);
+			factor *=
+				computeShadowFactorSpot(light.m_texProjectionMat, worldPos, light.m_lightDirRadius.w, u_shadowTex);
 		}
 #endif
 
@@ -124,21 +138,23 @@ Vec3 accumulateLights(U32 clusterIdx, Vec3 worldPos)
 
 void main()
 {
-	if(any(greaterThan(gl_GlobalInvocationID.xyz, VOLUME_SIZE)))
+	if(any(greaterThanEqual(gl_GlobalInvocationID.xyz, VOLUME_SIZE)))
 	{
 		return;
 	}
 
+	// Find a random pos inside the cluster
+	Vec3 worldPos = randWPos();
+
 	// Find the cluster
 	UVec3 clusterXYZ = gl_GlobalInvocationID / CLUSTER_COUNT;
-	U32 clusterIdx = clusterXYZ.z * (CLUSTER_COUNT.x * CLUSTER_COUNT.y) clusterXYZ.y * CLUSTER_COUNT.x + clusterXYZ.x;
-
-	// Find a random pos inside the cluster
-	Vec3 worldPos = randWPos(clusterXYZ.z);
+	U32 clusterIdx = clusterXYZ.z * (CLUSTER_COUNT.x * CLUSTER_COUNT.y) * clusterXYZ.y * CLUSTER_COUNT.x + clusterXYZ.x;
 
 	// Get lighting
 	Vec3 color = accumulateLights(clusterIdx, worldPos);
 
 	// Write result
 	imageStore(out_volume, IVec3(gl_GlobalInvocationID), Vec4(color, 0.0));
-}
+}
+
+#pragma anki end

+ 4 - 4
shaders/glsl_cpp_common/ClusteredShading.h

@@ -91,15 +91,15 @@ ANKI_SHADER_FUNC_INLINE U32 computeClusterIndex(
 }
 
 // Compute the Z of the near plane given a cluster idx
-ANKI_SHADER_FUNC_INLINE F32 computeClusterNear(ClustererMagicValues magic, U32 k)
+ANKI_SHADER_FUNC_INLINE F32 computeClusterNearf(ClustererMagicValues magic, F32 fk)
 {
-	F32 fk = F32(k);
 	return magic.m_val1.x() * fk * fk + magic.m_val1.y();
 }
 
-ANKI_SHADER_FUNC_INLINE F32 computeClusterFar(ClustererMagicValues magic, U32 k)
+// Compute the Z of the near plane given a cluster idx
+ANKI_SHADER_FUNC_INLINE F32 computeClusterNear(ClustererMagicValues magic, U32 k)
 {
-	return computeClusterNear(magic, k + 1u);
+	return computeClusterNearf(magic, F32(k));
 }
 
 ANKI_END_NAMESPACE

+ 1 - 0
src/anki/Renderer.h

@@ -29,5 +29,6 @@
 #include <anki/renderer/Tonemapping.h>
 #include <anki/renderer/RendererObject.h>
 #include <anki/renderer/Bloom.h>
+#include <anki/renderer/VolumetricLightingAccumulation.h>
 
 /// @defgroup renderer Renderering system

+ 2 - 1
src/anki/core/Config.cpp

@@ -20,7 +20,8 @@ Config::Config()
 	newOption("r.clusterSizeZ", 32);
 	newOption("r.avgObjectsPerCluster", 16);
 
-	newOption("r.volumetricLightingAccumulationClusterFraction", 2);
+	newOption("r.volumetricLightingAccumulation.clusterFraction", 2);
+	newOption("r.volumetricLightingAccumulation.finalClusterInZ", 16);
 
 	newOption("r.shadowMapping.enabled", true);
 	newOption("r.shadowMapping.resolution", 512);

+ 1 - 0
src/anki/renderer/Common.h

@@ -37,6 +37,7 @@ class DepthDownscale;
 class TemporalAA;
 class UiStage;
 class Ssr;
+class VolumetricLightingAccumulation;
 
 class RenderingContext;
 class DebugDrawer;

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

@@ -27,6 +27,7 @@
 #include <anki/renderer/TemporalAA.h>
 #include <anki/renderer/UiStage.h>
 #include <anki/renderer/Ssr.h>
+#include <anki/renderer/VolumetricLightingAccumulation.h>
 #include <shaders/glsl_cpp_common/ClusteredShading.h>
 
 namespace anki
@@ -113,6 +114,9 @@ Error Renderer::initInternal(const ConfigSet& config)
 	ANKI_CHECK(m_resources->loadResource("shaders/ClearTextureCompute.glslp", m_clearTexComputeProg));
 
 	// Init the stages. Careful with the order!!!!!!!!!!
+	m_volLighting.reset(m_alloc.newInstance<VolumetricLightingAccumulation>(this));
+	ANKI_CHECK(m_volLighting->init(config));
+
 	m_indirect.reset(m_alloc.newInstance<Indirect>(this));
 	ANKI_CHECK(m_indirect->init(config));
 
@@ -280,6 +284,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 
 	// Populate render graph. WARNING Watch the order
 	m_shadowMapping->populateRenderGraph(ctx);
+	m_volLighting->populateRenderGraph(ctx);
 	m_indirect->populateRenderGraph(ctx);
 	m_gbuffer->populateRenderGraph(ctx);
 	m_gbufferPost->populateRenderGraph(ctx);

+ 7 - 2
src/anki/renderer/Renderer.h

@@ -78,8 +78,7 @@ public:
 	Second m_lightBinTime ANKI_DBG_NULLIFY;
 };
 
-/// Offscreen renderer. It is a class and not a namespace because we may need external renderers for security cameras
-/// for example
+/// Offscreen renderer.
 class Renderer
 {
 public:
@@ -92,6 +91,11 @@ public:
 		return *m_indirect;
 	}
 
+	VolumetricLightingAccumulation& getVolumetricLightingAccumulation()
+	{
+		return *m_volLighting;
+	}
+
 	ShadowMapping& getShadowMapping()
 	{
 		return *m_shadowMapping;
@@ -367,6 +371,7 @@ private:
 
 	/// @name Rendering stages
 	/// @{
+	UniquePtr<VolumetricLightingAccumulation> m_volLighting;
 	UniquePtr<Indirect> m_indirect;
 	UniquePtr<ShadowMapping> m_shadowMapping; ///< Shadow mapping.
 	UniquePtr<GBuffer> m_gbuffer; ///< Material rendering stage

+ 14 - 0
src/anki/renderer/RendererObject.h

@@ -65,6 +65,20 @@ protected:
 		cmdb->dispatchCompute(sizeX, sizeY, 1);
 	}
 
+	static void dispatchPPCompute(CommandBufferPtr& cmdb,
+		U32 workgroupSizeX,
+		U32 workgroupSizeY,
+		U32 workgroupSizeZ,
+		U32 outImageWidth,
+		U32 outImageHeight,
+		U32 outImageDepth)
+	{
+		const U sizeX = (outImageWidth + workgroupSizeX - 1) / workgroupSizeX;
+		const U sizeY = (outImageHeight + workgroupSizeY - 1) / workgroupSizeY;
+		const U sizeZ = (outImageDepth + workgroupSizeZ - 1) / workgroupSizeZ;
+		cmdb->dispatchCompute(sizeX, sizeY, sizeZ);
+	}
+
 	template<typename TPtr>
 	TPtr allocateUniforms(PtrSize size, StagingGpuMemoryToken& token)
 	{

+ 2 - 1
src/anki/renderer/ShadowMapping.cpp

@@ -76,7 +76,8 @@ Error ShadowMapping::initEsm(const ConfigSet& cfg)
 		TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_atlasResolution,
 			m_atlasResolution,
 			SHADOW_COLOR_PIXEL_FORMAT,
-			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
+				| TextureUsageBit::SAMPLED_COMPUTE,
 			"esm");
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 		ClearValue clearVal;

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

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/renderer/VolumetricLightingAccumulation.h>
+#include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/misc/ConfigSet.h>
@@ -23,11 +24,14 @@ VolumetricLightingAccumulation::~VolumetricLightingAccumulation()
 Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 {
 	// Misc
-	const U fraction = config.getNumber("r.volumetricLightingAccumulationClusterFraction");
+	const U fraction = config.getNumber("r.volumetricLightingAccumulation.clusterFraction");
 	ANKI_ASSERT(fraction >= 1);
+	const U finalClusterZ = config.getNumber("r.volumetricLightingAccumulation.finalClusterInZ");
+	ANKI_ASSERT(finalClusterZ > 0 && finalClusterZ < m_r->getClusterCount()[2]);
+
 	m_volumeSize[0] = m_r->getClusterCount()[0] * fraction;
 	m_volumeSize[1] = m_r->getClusterCount()[1] * fraction;
-	m_volumeSize[2] = m_r->getClusterCount()[2] * fraction;
+	m_volumeSize[2] = (finalClusterZ + 1) * fraction;
 	ANKI_R_LOGI("Initializing volumetric lighting accumulation. Size %ux%ux%u",
 		m_volumeSize[0],
 		m_volumeSize[1],
@@ -41,11 +45,12 @@ Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 	ShaderProgramResourceMutationInitList<1> mutators(m_prog);
 	mutators.add("ENABLE_SHADOWS", 1);
 
-	ShaderProgramResourceConstantValueInitList<4> consts(m_prog);
+	ShaderProgramResourceConstantValueInitList<5> consts(m_prog);
 	consts.add("VOLUME_SIZE", UVec3(m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]))
 		.add("CLUSTER_COUNT", UVec3(m_r->getClusterCount()[0], m_r->getClusterCount()[1], m_r->getClusterCount()[2]))
-		.add("WORKGROUP_SIZE", UVec3(0, 0, 0)) // TODO
-		.add("NOISE_MAP_SIZE", UVec3(m_noiseTex->getWidth(), m_noiseTex->getHeight(), m_noiseTex->getDepth()));
+		.add("FINAL_CLUSTER_Z", U32(finalClusterZ))
+		.add("WORKGROUP_SIZE", UVec3(m_workgroupSize[0], m_workgroupSize[1], m_workgroupSize[2]))
+		.add("NOISE_TEX_SIZE", UVec3(m_noiseTex->getWidth(), m_noiseTex->getHeight(), m_noiseTex->getDepth()));
 
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
@@ -54,7 +59,7 @@ Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 	// Create RTs
 	TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_volumeSize[0],
 		m_volumeSize[1],
-		Format::R16G16B16A16_SFLOAT,
+		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
 		TextureUsageBit::IMAGE_COMPUTE_READ_WRITE | TextureUsageBit::SAMPLED_FRAGMENT,
 		"VolLight");
 	texinit.m_depth = m_volumeSize[2];
@@ -67,19 +72,59 @@ Error VolumetricLightingAccumulation::init(const ConfigSet& config)
 
 void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 {
+	m_runCtx.m_ctx = &ctx;
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
 	m_runCtx.m_rt = rgraph.importRenderTarget(m_rtTex, TextureUsageBit::SAMPLED_FRAGMENT);
 
 	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("Vol light");
-	pass.setWork(runCallback, this, 0);
+
+	auto callback = [](RenderPassWorkContext& rgraphCtx) -> void {
+		static_cast<VolumetricLightingAccumulation*>(rgraphCtx.m_userData)->run(rgraphCtx);
+	};
+	pass.setWork(callback, this, 0);
 
 	pass.newDependency({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_READ_WRITE});
+	pass.newDependency({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_COMPUTE});
 }
 
 void VolumetricLightingAccumulation::run(RenderPassWorkContext& rgraphCtx)
 {
-	// TODO
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	RenderingContext& ctx = *m_runCtx.m_ctx;
+
+	cmdb->bindShaderProgram(m_grProg);
+
+	rgraphCtx.bindImage(0, 0, m_runCtx.m_rt, TextureSubresourceInfo());
+
+	cmdb->bindTextureAndSampler(
+		0, 0, m_noiseTex->getGrTextureView(), m_r->getTrilinearRepeatSampler(), TextureUsageBit::SAMPLED_COMPUTE);
+
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
+
+	const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
+	bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
+	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
+	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
+	bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
+
+	struct PushConsts
+	{
+		Vec4 m_noiseOffsetPad3;
+	} regs;
+	regs.m_noiseOffsetPad3 = Vec4(0.0f);
+	regs.m_noiseOffsetPad3.x() = 1.0f / F32(m_r->getFrameCount() % m_noiseTex->getDepth()) * 0.5f;
+
+	cmdb->setPushConstants(&regs, sizeof(regs));
+
+	dispatchPPCompute(cmdb,
+		m_workgroupSize[0],
+		m_workgroupSize[1],
+		m_workgroupSize[2],
+		m_volumeSize[0],
+		m_volumeSize[1],
+		m_volumeSize[2]);
 }
 
 } // end namespace anki

+ 2 - 5
src/anki/renderer/VolumetricLightingAccumulation.h

@@ -31,6 +31,7 @@ anki_internal:
 	}
 
 private:
+	Array<U32, 3> m_workgroupSize = {{8, 8, 8}};
 	Array<U32, 3> m_volumeSize;
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
@@ -41,14 +42,10 @@ private:
 	class
 	{
 	public:
+		RenderingContext* m_ctx = nullptr;
 		RenderTargetHandle m_rt;
 	} m_runCtx; ///< Runtime context.
 
-	static void runCallback(RenderPassWorkContext& rgraphCtx)
-	{
-		static_cast<VolumetricLightingAccumulation*>(rgraphCtx.m_userData)->run(rgraphCtx);
-	}
-
 	void run(RenderPassWorkContext& rgraphCtx);
 };
 /// @}