Browse Source

Volumetric fog is feature complete

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
c185dcde6b

+ 1 - 1
samples/common/Framework.cpp

@@ -32,7 +32,7 @@ Error SampleApp::init(int argc, char** argv, CString sampleName)
 	getInput().moveCursor(Vec2(0.0f));
 
 	// Some renderer stuff
-	getMainRenderer().getOffscreenRenderer().getVolumetric().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f) * 0.0001f);
+	getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f) * 0.0001f);
 
 	ANKI_CHECK(sampleExtraInit());
 

+ 1 - 1
samples/sponza/Main.cpp

@@ -17,7 +17,7 @@ public:
 		ANKI_CHECK(getResourceManager().loadResource("assets/scene.lua", script));
 		ANKI_CHECK(getScriptManager().evalString(script->getSource()));
 
-		getMainRenderer().getOffscreenRenderer().getVolumetric().setFogParticleColor(Vec3(1.0, 0.9, 0.9) * 0.009);
+		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0, 0.9, 0.9) * 0.009);
 		return Error::NONE;
 	}
 };

+ 1 - 1
sandbox/Main.cpp

@@ -45,7 +45,7 @@ Error MyApp::init(int argc, char* argv[])
 	MainRenderer& renderer = getMainRenderer();
 	ResourceManager& resources = getResourceManager();
 
-	renderer.getOffscreenRenderer().getVolumetric().setFogParticleColor(Vec3(1.0, 0.9, 0.9) * 0.009);
+	renderer.getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0, 0.9, 0.9) * 0.009);
 
 	if(getenv("PROFILE"))
 	{

+ 0 - 48
shaders/ForwardShadingVolumetricUpscale.glslp

@@ -1,48 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma anki input const Vec2 SRC_SIZE
-#pragma anki input const Vec2 FB_SIZE
-#pragma anki input const U32 NOISE_TEX_SIZE
-
-#pragma anki start vert
-#include <shaders/QuadVert.glsl>
-#pragma anki end
-
-#pragma anki start frag
-#include <shaders/Functions.glsl>
-
-#define BLUE_NOISE 0
-
-layout(location = 0) in Vec2 in_uv;
-layout(location = 0) out Vec4 out_color;
-
-layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
-layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
-layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_colorTex;
-#if BLUE_NOISE
-layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2DArray u_noiseTex;
-#endif
-
-layout(ANKI_UBO_BINDING(0, 0)) uniform u0_
-{
-	Vec4 u_linearizeCfPad2;
-};
-
-void main()
-{
-	Vec3 col =
-		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_colorTex, 1.0 / SRC_SIZE, in_uv, u_linearizeCfPad2.xy).rgb;
-
-#if BLUE_NOISE
-	Vec3 blueNoise = texture(u_noiseTex, Vec3(FB_SIZE / Vec2(NOISE_TEX_SIZE) * in_uv, 0.0), 0.0).rgb;
-	blueNoise = blueNoise * 2.0 - 1.0;
-	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
-	col += blueNoise / 16.0;
-#endif
-
-	out_color = Vec4(col, 0.0);
-}
-#pragma anki end

+ 49 - 0
shaders/LightShadingApplyFog.glslp

@@ -0,0 +1,49 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki input const UVec3 FOG_VOLUME_SIZE
+
+#pragma anki start vert
+#include <shaders/QuadVert.glsl>
+#pragma anki end
+
+#pragma anki start frag
+
+#include <shaders/Common.glsl>
+
+layout(location = 0) in Vec2 in_uv;
+layout(location = 0) out Vec3 out_color;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthRt;
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler3D u_fogVolume;
+
+struct PushConsts
+{
+	ClustererMagicValues m_clustererMagic;
+	Mat4 m_invViewProjMat;
+};
+ANKI_PUSH_CONSTANTS(PushConsts, u_regs);
+#define u_clustererMagic u_regs.m_clustererMagic
+#define u_invViewProjMat u_regs.m_invViewProjMat
+
+void main()
+{
+	F32 depth = textureLod(u_depthRt, in_uv, 0.0).r;
+
+	// Get world position
+	Vec4 worldPos4 = u_invViewProjMat * Vec4(ndc, depth, 1.0);
+	Vec3 worldPos = worldPos4.xyz / worldPos4.w;
+
+	// Read the volume
+	Vec3 uv3d = computeClustererVolumeTextureUvs(u_clustererMagic, uv, worldPos, FOG_VOLUME_SIZE.z);
+	Vec4 fogVals = textureLod(u_fogVolume, uv3d, 0.0);
+	Vec3 inScattering = fogVals.rgb;
+	F32 transmittance = fogVals.a;
+
+	// Apply the fog
+	out_color = Vec4(inScattering, transmittance);
+}
+
+#pragma anki end

+ 76 - 0
shaders/VolumetricFogAccumulation.glslp

@@ -0,0 +1,76 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki input const UVec2 WORKGROUP_SIZE
+#pragma anki input const UVec3 VOLUME_SIZE
+#pragma anki input const UVec3 CLUSTER_COUNT
+#pragma anki input const UVec3 FRACTION
+#pragma anki input const U32 FINAL_CLUSTER_Z
+
+#pragma anki start comp
+
+#include <shaders/glsl_cpp_common/ClusteredShading.h>
+
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler3D u_lightVolume;
+layout(ANKI_IMAGE_BINDING(0, 0)) writeonly uniform image3D u_fogVolume;
+
+struct PushConsts
+{
+	Vec4 m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1;
+	Vec4 m_fogDiffusePad1;
+	ClustererMagicValues m_clustererMagic;
+};
+ANKI_PUSH_CONSTANTS(PushConsts, u_regs);
+#define u_fogScatteringCoeff u_regs.m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1.x
+#define u_fogAbsorptionCoeff u_regs.m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1.y
+#define u_density u_regs.m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1.z
+#define u_fogDiffuse u_regs.m_fogDiffusePad1.xyz
+#define u_clustererMagic m_regs.m_clustererMagic
+
+void main()
+{
+	if(any(greaterThanEqual(gl_GlobalInvocationID.xy, VOLUME_SIZE.xy)))
+	{
+		return;
+	}
+
+	Vec2 uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(VOLUME_SIZE.xy);
+
+	Vec4 colorAndDensityFront = Vec4(0.0);
+	ANKI_LOOP for(U32 i = 0u; i <= VOLUME_SIZE.z; ++i)
+	{
+		// Compute the cluster K limits of this cluster fragment
+		F32 clusterKNear = F32(i) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
+		F32 clusterKFar = F32(i + 1u) * (F32(FINAL_CLUSTER_Z + 1u) / F32(VOLUME_SIZE.z));
+
+		// Compute the min and max Z in view space if this cluster fragment
+		F32 zVSpaceNear = -computeClusterNearf(u_clustererMagic, clusterKNear);
+		F32 zVSpaceFar = -computeClusterNearf(u_clustererMagic, clusterKFar);
+
+		// Compute the thikness of this fragment
+		F32 layerThinkness = abs(zVSpaceNear - zVSpaceFar);
+
+		// Scattering & absorption
+		F32 scattering = u_density * u_fogScatteringCoeff * layerThinkness;
+		F32 absorption = u_density * u_fogAbsorptionCoeff * layerThinkness;
+
+		// Read the light value
+		F32 k = mix(clusterKFar, clusterKNear, 0.5);
+		Vec3 light = textureLod(u_lightVolume, Vec3(uv, k / F32(FINAL_CLUSTER_Z + 1u)), 0.0).rgb;
+		light *= u_fogDiffuse;
+
+		// Integrate
+		Vec4 colorAndDensityBack = Vec4(light * scattering, scattering * absoption);
+
+		Vec3 l = colorAndDensityFront.rgb + saturate(exp(-colorAndDensityFront.a)) * colorAndDensityBack.rgb;
+		colorAndDensityFront = Vec4(l.rgb, colorAndDensityFront.a + colorAndDensityBack.a);
+
+		// Write the value
+		Vec4 valToWrite = Vec4(colorAndDensityFront.rgb, exp(-colorAndDensityFront.a));
+		imageStore(u_fogVolume, IVec3(UVec3(gl_GlobalInvocationID.xy, i)), valToWrite);
+	}
+}

+ 1 - 1
src/anki/Renderer.h

@@ -8,7 +8,7 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/Common.h>
 #include <anki/renderer/LightShading.h>
-#include <anki/renderer/Volumetric.h>
+#include <anki/renderer/VolumetricFog.h>
 #include <anki/renderer/ForwardShading.h>
 #include <anki/renderer/FinalComposite.h>
 #include <anki/renderer/ClusterBin.h>

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

@@ -32,7 +32,7 @@ class FinalComposite;
 class Dbg;
 class Indirect;
 class DownscaleBlur;
-class Volumetric;
+class VolumetricFog;
 class DepthDownscale;
 class TemporalAA;
 class UiStage;

+ 2 - 72
src/anki/renderer/ForwardShading.cpp

@@ -9,7 +9,6 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/ShadowMapping.h>
-#include <anki/renderer/Volumetric.h>
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/LensFlare.h>
 #include <anki/renderer/VolumetricLightingAccumulation.h>
@@ -24,72 +23,9 @@ ForwardShading::~ForwardShading()
 Error ForwardShading::init(const ConfigSet& cfg)
 {
 	ANKI_R_LOGI("Initializing forward shading");
-
-	Error err = initInternal(cfg);
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize forward shading");
-	}
-
-	return err;
-}
-
-Error ForwardShading::initInternal(const ConfigSet&)
-{
-	ANKI_CHECK(initVol());
-
-	return Error::NONE;
-}
-
-Error ForwardShading::initVol()
-{
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_vol.m_noiseTex));
-
-	ANKI_CHECK(getResourceManager().loadResource("shaders/ForwardShadingVolumetricUpscale.glslp", m_vol.m_prog));
-
-	ShaderProgramResourceConstantValueInitList<3> consts(m_vol.m_prog);
-	consts.add("NOISE_TEX_SIZE", U32(m_vol.m_noiseTex->getWidth()))
-		.add("SRC_SIZE", Vec2(m_r->getWidth() / VOLUMETRIC_FRACTION, m_r->getHeight() / VOLUMETRIC_FRACTION))
-		.add("FB_SIZE", Vec2(m_r->getWidth(), m_r->getHeight()));
-
-	const ShaderProgramResourceVariant* variant;
-	m_vol.m_prog->getOrCreateVariant(consts.get(), variant);
-	m_vol.m_grProg = variant->getProgram();
-
 	return Error::NONE;
 }
 
-void ForwardShading::drawVolumetric(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
-	cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
-	cmdb->setDepthWrite(false);
-
-	cmdb->bindShaderProgram(m_vol.m_grProg);
-	Vec4* unis = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
-	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, unis->x(), unis->y());
-
-	rgraphCtx.bindTextureAndSampler(
-		0, 0, m_r->getDepthDownscale().getHiZRt(), HIZ_HALF_DEPTH, m_r->getNearestSampler());
-	rgraphCtx.bindTextureAndSampler(
-		0, 1, m_r->getDepthDownscale().getHiZRt(), HIZ_QUARTER_DEPTH, m_r->getNearestSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getVolumetric().getRt(), m_r->getLinearSampler());
-	cmdb->bindTextureAndSampler(0,
-		3,
-		m_vol.m_noiseTex->getGrTextureView(),
-		m_r->getTrilinearRepeatSampler(),
-		TextureUsageBit::SAMPLED_FRAGMENT);
-
-	drawQuad(cmdb);
-
-	// Restore state
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
-	cmdb->setDepthCompareOperation(CompareOperation::LESS);
-	cmdb->setDepthWrite(true);
-}
-
 void ForwardShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
@@ -130,14 +66,9 @@ void ForwardShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgr
 		cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
 	}
 
-	if(threadId == threadCount - 1)
+	if(threadId == threadCount - 1 && ctx.m_renderQueue->m_lensFlares.getSize())
 	{
-		drawVolumetric(ctx, rgraphCtx);
-
-		if(ctx.m_renderQueue->m_lensFlares.getSize())
-		{
-			m_r->getLensFlare().runDrawFlares(ctx, cmdb);
-		}
+		m_r->getLensFlare().runDrawFlares(ctx, cmdb);
 	}
 }
 
@@ -145,7 +76,6 @@ void ForwardShading::setDependencies(const RenderingContext& ctx, GraphicsRender
 {
 	pass.newDependency({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_HALF_DEPTH});
 	pass.newDependency({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_QUARTER_DEPTH});
-	pass.newDependency({m_r->getVolumetric().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 	pass.newDependency({m_r->getVolumetricLightingAccumulation().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 
 	if(ctx.m_renderQueue->m_lensFlares.getSize())

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

@@ -29,20 +29,6 @@ anki_internal:
 	void setDependencies(const RenderingContext& ctx, GraphicsRenderPassDescription& pass);
 
 	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
-
-private:
-	class Vol
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-		TextureResourcePtr m_noiseTex;
-	} m_vol;
-
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
-	ANKI_USE_RESULT Error initVol();
-
-	void drawVolumetric(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 };
 /// @}
 

+ 73 - 16
src/anki/renderer/LightShading.cpp

@@ -10,6 +10,7 @@
 #include <anki/renderer/GBuffer.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/ForwardShading.h>
+#include <anki/renderer/VolumetricFog.h>
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Ssr.h>
@@ -31,7 +32,12 @@ LightShading::~LightShading()
 Error LightShading::init(const ConfigSet& config)
 {
 	ANKI_R_LOGI("Initializing light stage");
-	Error err = initInternal(config);
+	Error err = initLightShading(config);
+
+	if(!err)
+	{
+		err = initApplyFog(config);
+	}
 
 	if(err)
 	{
@@ -41,31 +47,49 @@ Error LightShading::init(const ConfigSet& config)
 	return err;
 }
 
-Error LightShading::initInternal(const ConfigSet& config)
+Error LightShading::initLightShading(const ConfigSet& config)
 {
 	// Load shaders and programs
-	ANKI_CHECK(getResourceManager().loadResource("shaders/LightShading.glslp", m_prog));
+	ANKI_CHECK(getResourceManager().loadResource("shaders/LightShading.glslp", m_lightShading.m_prog));
 
-	ShaderProgramResourceConstantValueInitList<5> consts(m_prog);
+	ShaderProgramResourceConstantValueInitList<5> consts(m_lightShading.m_prog);
 	consts.add("CLUSTER_COUNT_X", U32(m_r->getClusterCount()[0]))
 		.add("CLUSTER_COUNT_Y", U32(m_r->getClusterCount()[1]))
 		.add("CLUSTER_COUNT_Z", U32(m_r->getClusterCount()[2]))
 		.add("CLUSTER_COUNT", U32(m_r->getClusterCount()[3]))
 		.add("IR_MIPMAP_COUNT", U32(m_r->getIndirect().getReflectionTextureMipmapCount()));
 
-	m_prog->getOrCreateVariant(consts.get(), m_progVariant);
+	const ShaderProgramResourceVariant* variant;
+	m_lightShading.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_lightShading.m_grProg = variant->getProgram();
 
 	// Create RT descr
-	m_rtDescr = m_r->create2DRenderTargetDescription(
+	m_lightShading.m_rtDescr = m_r->create2DRenderTargetDescription(
 		m_r->getWidth(), m_r->getHeight(), LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT, "Light Shading");
-	m_rtDescr.bake();
+	m_lightShading.m_rtDescr.bake();
 
 	// Create FB descr
-	m_fbDescr.m_colorAttachmentCount = 1;
-	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
-	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
-	m_fbDescr.bake();
+	m_lightShading.m_fbDescr.m_colorAttachmentCount = 1;
+	m_lightShading.m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	m_lightShading.m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::LOAD;
+	m_lightShading.m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
+	m_lightShading.m_fbDescr.bake();
+
+	return Error::NONE;
+}
+
+Error LightShading::initApplyFog(const ConfigSet& config)
+{
+	// Load shaders and programs
+	ANKI_CHECK(getResourceManager().loadResource("shaders/LightShadingApplyFog.glslp", m_applyFog.m_prog));
+
+	ShaderProgramResourceConstantValueInitList<1> consts(m_applyFog.m_prog);
+	const auto& volSize = m_r->getVolumetricFog().getVolumeSize();
+	consts.add("VOLUMETRIC_SIZE", UVec3(volSize[0], volSize[1], volSize[2]));
+
+	const ShaderProgramResourceVariant* variant;
+	m_applyFog.m_prog->getOrCreateVariant(consts.get(), variant);
+	m_applyFog.m_grProg = variant->getProgram();
 
 	return Error::NONE;
 }
@@ -81,7 +105,7 @@ void LightShading::run(RenderPassWorkContext& rgraphCtx)
 	// Do light shading first
 	if(rgraphCtx.m_currentSecondLevelCommandBufferIndex == 0)
 	{
-		cmdb->bindShaderProgram(m_progVariant->getProgram());
+		cmdb->bindShaderProgram(m_lightShading.m_grProg);
 		cmdb->setDepthWrite(false);
 
 		// Bind textures
@@ -120,7 +144,40 @@ void LightShading::run(RenderPassWorkContext& rgraphCtx)
 		drawQuad(cmdb);
 	}
 
-	// Forward shading
+	// Do the fog apply
+	if(rgraphCtx.m_currentSecondLevelCommandBufferIndex == rgraphCtx.m_secondLevelCommandBufferCount - 1u)
+	{
+		cmdb->bindShaderProgram(m_applyFog.m_grProg);
+
+		// Bind textures
+		rgraphCtx.bindTextureAndSampler(0,
+			0,
+			m_r->getGBuffer().getDepthRt(),
+			TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+			m_r->getNearestSampler());
+		rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getVolumetricFog().getRt(), m_r->getLinearSampler());
+
+		// Uniforms
+		struct PushConsts
+		{
+			ClustererMagicValues m_clustererMagic;
+			Mat4 m_invViewProjMat;
+		} regs;
+		regs.m_clustererMagic = ctx.m_clusterBinOut.m_shaderMagicValues;
+		regs.m_invViewProjMat = ctx.m_matrices.m_viewProjectionJitter.getInverse();
+
+		cmdb->setPushConstants(&regs, sizeof(regs));
+
+		// finalPixelColor = pixelWithoutFog * transmitance + inScattering (see the shader)
+		cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
+
+		drawQuad(cmdb);
+
+		// Reset state
+		cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
+	}
+
+	// Forward shading last
 	m_r->getForwardShading().run(ctx, rgraphCtx);
 }
 
@@ -130,7 +187,7 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 
 	// Create RT
-	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_lightShading.m_rtDescr);
 
 	// Create pass
 	GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("Light&FW Shad.");
@@ -139,7 +196,7 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 		[](RenderPassWorkContext& rgraphCtx) { static_cast<LightShading*>(rgraphCtx.m_userData)->run(rgraphCtx); },
 		this,
 		computeNumberOfSecondLevelCommandBuffers(ctx.m_renderQueue->m_forwardShadingRenderables.getSize()));
-	pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rt}}, {m_r->getGBuffer().getDepthRt()});
+	pass.setFramebufferInfo(m_lightShading.m_fbDescr, {{m_runCtx.m_rt}}, {m_r->getGBuffer().getDepthRt()});
 
 	// Light shading
 	pass.newDependency({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});

+ 18 - 7
src/anki/renderer/LightShading.h

@@ -33,12 +33,23 @@ anki_internal:
 	}
 
 private:
-	RenderTargetDescription m_rtDescr;
-	FramebufferDescription m_fbDescr;
+	class
+	{
+	public:
+		RenderTargetDescription m_rtDescr;
+		FramebufferDescription m_fbDescr;
+
+		// Light shaders
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_lightShading;
 
-	// Light shaders
-	ShaderProgramResourcePtr m_prog;
-	const ShaderProgramResourceVariant* m_progVariant = nullptr;
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+	} m_applyFog;
 
 	class
 	{
@@ -47,8 +58,8 @@ private:
 		RenderingContext* m_ctx;
 	} m_runCtx; ///< Run context.
 
-	/// Called by init
-	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
+	ANKI_USE_RESULT Error initLightShading(const ConfigSet& config);
+	ANKI_USE_RESULT Error initApplyFog(const ConfigSet& config);
 
 	void run(RenderPassWorkContext& rgraphCtx);
 };

+ 4 - 4
src/anki/renderer/Renderer.cpp

@@ -22,7 +22,7 @@
 #include <anki/renderer/LensFlare.h>
 #include <anki/renderer/Dbg.h>
 #include <anki/renderer/DownscaleBlur.h>
-#include <anki/renderer/Volumetric.h>
+#include <anki/renderer/VolumetricFog.h>
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/TemporalAA.h>
 #include <anki/renderer/UiStage.h>
@@ -135,8 +135,8 @@ Error Renderer::initInternal(const ConfigSet& config)
 	m_depth.reset(m_alloc.newInstance<DepthDownscale>(this));
 	ANKI_CHECK(m_depth->init(config));
 
-	m_vol.reset(m_alloc.newInstance<Volumetric>(this));
-	ANKI_CHECK(m_vol->init(config));
+	m_volFog.reset(m_alloc.newInstance<VolumetricFog>(this));
+	ANKI_CHECK(m_volFog->init(config));
 
 	m_forwardShading.reset(m_alloc.newInstance<ForwardShading>(this));
 	ANKI_CHECK(m_forwardShading->init(config));
@@ -289,7 +289,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_gbuffer->populateRenderGraph(ctx);
 	m_gbufferPost->populateRenderGraph(ctx);
 	m_depth->populateRenderGraph(ctx);
-	m_vol->populateRenderGraph(ctx);
+	m_volFog->populateRenderGraph(ctx);
 	m_ssao->populateRenderGraph(ctx);
 	m_lensFlare->populateRenderGraph(ctx);
 	m_ssr->populateRenderGraph(ctx);

+ 3 - 3
src/anki/renderer/Renderer.h

@@ -122,9 +122,9 @@ public:
 		return *m_forwardShading;
 	}
 
-	Volumetric& getVolumetric()
+	VolumetricFog& getVolumetricFog()
 	{
-		return *m_vol;
+		return *m_volFog;
 	}
 
 	Tonemapping& getTonemapping()
@@ -381,7 +381,7 @@ private:
 	UniquePtr<LightShading> m_lightShading; ///< Illumination rendering stage
 	UniquePtr<DepthDownscale> m_depth;
 	UniquePtr<ForwardShading> m_forwardShading; ///< Forward shading.
-	UniquePtr<Volumetric> m_vol; ///< Volumetric effects.
+	UniquePtr<VolumetricFog> m_volFog; ///< Volumetric fog.
 	UniquePtr<LensFlare> m_lensFlare; ///< Forward shading lens flares.
 	UniquePtr<DownscaleBlur> m_downscale;
 	UniquePtr<TemporalAA> m_temporalAA;

+ 0 - 238
src/anki/renderer/Volumetric.cpp

@@ -1,238 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/renderer/Volumetric.h>
-#include <anki/renderer/Renderer.h>
-#include <anki/renderer/DepthDownscale.h>
-#include <anki/renderer/ShadowMapping.h>
-#include <anki/renderer/LightShading.h>
-#include <anki/renderer/RenderQueue.h>
-
-namespace anki
-{
-
-Error Volumetric::initMain(const ConfigSet& config)
-{
-	// Misc
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_main.m_noiseTex));
-
-	// Shaders
-	ANKI_CHECK(getResourceManager().loadResource("shaders/VolumetricFog.glslp", m_main.m_prog));
-
-	ShaderProgramResourceMutationInitList<1> mutators(m_main.m_prog);
-	mutators.add("ENABLE_SHADOWS", 1);
-
-	ShaderProgramResourceConstantValueInitList<3> consts(m_main.m_prog);
-	consts.add("FB_SIZE", UVec2(m_width, m_height))
-		.add("CLUSTER_COUNT", UVec3(m_r->getClusterCount()[0], m_r->getClusterCount()[1], m_r->getClusterCount()[2]))
-		.add("NOISE_MAP_SIZE", U32(m_main.m_noiseTex->getWidth()));
-
-	const ShaderProgramResourceVariant* variant;
-	m_main.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_main.m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
-Error Volumetric::initHBlur(const ConfigSet& config)
-{
-	// Progs
-	ANKI_CHECK(m_r->getResourceManager().loadResource("shaders/LumaAwareBlur.glslp", m_hblur.m_prog));
-
-	ShaderProgramResourceMutationInitList<3> mutators(m_hblur.m_prog);
-	mutators.add("HORIZONTAL", 1).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_hblur.m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
-
-	const ShaderProgramResourceVariant* variant;
-	m_hblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_hblur.m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
-Error Volumetric::initVBlur(const ConfigSet& config)
-{
-	// Progs
-	ANKI_CHECK(m_r->getResourceManager().loadResource("shaders/LumaAwareBlur.glslp", m_vblur.m_prog));
-
-	ShaderProgramResourceMutationInitList<3> mutators(m_vblur.m_prog);
-	mutators.add("HORIZONTAL", 0).add("KERNEL_SIZE", 11).add("COLOR_COMPONENTS", 3);
-	ShaderProgramResourceConstantValueInitList<1> consts(m_vblur.m_prog);
-	consts.add("TEXTURE_SIZE", UVec2(m_width, m_height));
-
-	const ShaderProgramResourceVariant* variant;
-	m_vblur.m_prog->getOrCreateVariant(mutators.get(), consts.get(), variant);
-	m_vblur.m_grProg = variant->getProgram();
-
-	return Error::NONE;
-}
-
-Error Volumetric::init(const ConfigSet& config)
-{
-	m_width = m_r->getWidth() / VOLUMETRIC_FRACTION;
-	m_height = m_r->getHeight() / VOLUMETRIC_FRACTION;
-
-	ANKI_R_LOGI("Initializing volumetric pass. Size %ux%u", m_width, m_height);
-
-	for(U i = 0; i < 2; ++i)
-	{
-		// RT
-		TextureInitInfo rtInit = m_r->create2DRenderTargetInitInfo(m_width,
-			m_height,
-			LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
-			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
-			"Volumetric");
-		rtInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
-		m_rtTextures[i] = m_r->createAndClearRenderTarget(rtInit);
-	}
-
-	// FB
-	m_fbDescr.m_colorAttachmentCount = 1;
-	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fbDescr.bake();
-
-	Error err = initMain(config);
-
-	if(!err)
-	{
-		err = initHBlur(config);
-	}
-
-	if(!err)
-	{
-		err = initVBlur(config);
-	}
-
-	if(err)
-	{
-		ANKI_R_LOGE("Failed to initialize volumetric pass");
-	}
-
-	return err;
-}
-
-void Volumetric::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	cmdb->setViewport(0, 0, m_width, m_height);
-
-	rgraphCtx.bindTextureAndSampler(
-		0, 0, m_r->getDepthDownscale().getHiZRt(), HIZ_QUARTER_DEPTH, m_r->getLinearSampler());
-
-	cmdb->bindTextureAndSampler(0,
-		1,
-		m_main.m_noiseTex->getGrTextureView(),
-		m_r->getTrilinearRepeatSampler(),
-		TextureUsageBit::SAMPLED_FRAGMENT);
-	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
-	rgraphCtx.bindColorTextureAndSampler(0, 3, 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);
-
-	struct Unis
-	{
-		Vec4 m_linearizeNoiseTexOffsetLayer;
-		Vec4 m_fogParticleColorPad1;
-	};
-
-	Unis* uniforms = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 3);
-	computeLinearizeDepthOptimal(ctx.m_renderQueue->m_cameraNear,
-		ctx.m_renderQueue->m_cameraFar,
-		uniforms->m_linearizeNoiseTexOffsetLayer.x(),
-		uniforms->m_linearizeNoiseTexOffsetLayer.y());
-	F32 texelOffset = 1.0 / m_main.m_noiseTex->getWidth();
-	uniforms->m_linearizeNoiseTexOffsetLayer.z() = m_r->getFrameCount() * texelOffset;
-	uniforms->m_linearizeNoiseTexOffsetLayer.w() = m_r->getFrameCount() & (m_main.m_noiseTex->getLayerCount() - 1);
-	uniforms->m_fogParticleColorPad1 = Vec4(m_main.m_fogParticleColor, 0.0);
-
-	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
-
-	cmdb->bindShaderProgram(m_main.m_grProg);
-
-	drawQuad(cmdb);
-}
-
-void Volumetric::runHBlur(RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[m_r->getFrameCount() & 1], m_r->getLinearSampler());
-	cmdb->bindShaderProgram(m_hblur.m_grProg);
-	cmdb->setViewport(0, 0, m_width, m_height);
-
-	drawQuad(cmdb);
-}
-
-void Volumetric::runVBlur(RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
-	cmdb->bindShaderProgram(m_vblur.m_grProg);
-	cmdb->setViewport(0, 0, m_width, m_height);
-
-	drawQuad(cmdb);
-}
-
-void Volumetric::populateRenderGraph(RenderingContext& ctx)
-{
-	m_runCtx.m_ctx = &ctx;
-	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
-
-	// Create RTs
-	const U rtToRenderIdx = m_r->getFrameCount() & 1;
-	m_runCtx.m_rts[rtToRenderIdx] = rgraph.importRenderTarget(m_rtTextures[rtToRenderIdx], TextureUsageBit::NONE);
-	const U rtToReadIdx = !rtToRenderIdx;
-	m_runCtx.m_rts[rtToReadIdx] =
-		rgraph.importRenderTarget(m_rtTextures[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT);
-
-	// Create main render pass
-	{
-		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL main");
-
-		pass.setWork(runMainCallback, this, 0);
-		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
-
-		pass.newDependency({m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_FRAGMENT, HIZ_QUARTER_DEPTH});
-		pass.newDependency({m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::SAMPLED_FRAGMENT});
-		pass.newDependency({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newDependency({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
-	}
-
-	// Create HBlur pass
-	{
-		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL hblur");
-
-		pass.setWork(runHBlurCallback, this, 0);
-		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToReadIdx]}}, {});
-
-		pass.newDependency({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newDependency({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::SAMPLED_FRAGMENT});
-	}
-
-	// Create VBlur pass
-	{
-		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("VOL vblur");
-
-		pass.setWork(runVBlurCallback, this, 0);
-		pass.setFramebufferInfo(m_fbDescr, {{m_runCtx.m_rts[rtToRenderIdx]}}, {});
-
-		pass.newDependency({m_runCtx.m_rts[rtToRenderIdx], TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newDependency({m_runCtx.m_rts[rtToReadIdx], TextureUsageBit::SAMPLED_FRAGMENT});
-	}
-}
-
-RenderTargetHandle Volumetric::getRt() const
-{
-	return m_runCtx.m_rts[m_r->getFrameCount() & 1];
-}
-
-} // end namespace anki

+ 0 - 113
src/anki/renderer/Volumetric.h

@@ -1,113 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/renderer/RendererObject.h>
-
-namespace anki
-{
-
-/// @addtogroup renderer
-/// @{
-
-/// Volumetric effects.
-class Volumetric : public RendererObject
-{
-public:
-	void setFogParticleColor(const Vec3& col)
-	{
-		m_main.m_fogParticleColor = col;
-	}
-
-anki_internal:
-
-	Volumetric(Renderer* r)
-		: RendererObject(r)
-	{
-	}
-
-	~Volumetric()
-	{
-	}
-
-	ANKI_USE_RESULT Error init(const ConfigSet& config);
-
-	/// Populate the rendergraph.
-	void populateRenderGraph(RenderingContext& ctx);
-
-	RenderTargetHandle getRt() const;
-
-private:
-	U32 m_width = 0, m_height = 0;
-
-	class
-	{
-	public:
-		Vec3 m_fogParticleColor = Vec3(1.0);
-		Mat3x4 m_prevCameraRot = Mat3x4::getIdentity();
-
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-
-		TextureResourcePtr m_noiseTex;
-	} m_main; ///< Main noisy pass.
-
-	class
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-	} m_hblur; ///< Horizontal blur.
-
-	class
-	{
-	public:
-		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-	} m_vblur; ///< Vertical blur.
-
-	class
-	{
-	public:
-		Array<RenderTargetHandle, 2> m_rts;
-		const RenderingContext* m_ctx = nullptr;
-	} m_runCtx; ///< Runtime context.
-
-	Array<TexturePtr, 2> m_rtTextures;
-	FramebufferDescription m_fbDescr;
-
-	ANKI_USE_RESULT Error initMain(const ConfigSet& set);
-	ANKI_USE_RESULT Error initVBlur(const ConfigSet& set);
-	ANKI_USE_RESULT Error initHBlur(const ConfigSet& set);
-
-	void runMain(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
-	void runHBlur(RenderPassWorkContext& rgraphCtx);
-	void runVBlur(RenderPassWorkContext& rgraphCtx);
-
-	/// A RenderPassWorkCallback for SSAO main pass.
-	static void runMainCallback(RenderPassWorkContext& rgraphCtx)
-	{
-		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
-		self->runMain(*self->m_runCtx.m_ctx, rgraphCtx);
-	}
-
-	/// A RenderPassWorkCallback for SSAO HBlur.
-	static void runHBlurCallback(RenderPassWorkContext& rgraphCtx)
-	{
-		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
-		self->runHBlur(rgraphCtx);
-	}
-
-	/// A RenderPassWorkCallback for SSAO VBlur.
-	static void runVBlurCallback(RenderPassWorkContext& rgraphCtx)
-	{
-		Volumetric* const self = scast<Volumetric*>(rgraphCtx.m_userData);
-		self->runVBlur(rgraphCtx);
-	}
-};
-/// @}
-
-} // end namespace anki

+ 102 - 0
src/anki/renderer/VolumetricFog.cpp

@@ -0,0 +1,102 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/renderer/VolumetricFog.h>
+#include <anki/renderer/Renderer.h>
+#include <anki/renderer/DepthDownscale.h>
+#include <anki/renderer/ShadowMapping.h>
+#include <anki/renderer/LightShading.h>
+#include <anki/renderer/RenderQueue.h>
+#include <anki/renderer/VolumetricLightingAccumulation.h>
+#include <anki/misc/ConfigSet.h>
+
+namespace anki
+{
+
+Error VolumetricFog::init(const ConfigSet& config)
+{
+	// Misc
+	const U fractionXY = config.getNumber("r.volumetricLightingAccumulation.clusterFractionXY");
+	ANKI_ASSERT(fractionXY >= 1);
+	const U fractionZ = config.getNumber("r.volumetricLightingAccumulation.clusterFractionZ");
+	ANKI_ASSERT(fractionZ >= 1);
+	m_finalClusterZ = config.getNumber("r.volumetricLightingAccumulation.finalClusterInZ");
+	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;
+	ANKI_R_LOGI("Initializing volumetric fog. Size %ux%ux%u", m_volumeSize[0], m_volumeSize[1], m_volumeSize[2]);
+
+	// Shaders
+	ANKI_CHECK(getResourceManager().loadResource("shaders/VolumetricFogAccumulation.glslp", 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("FINAL_CLUSTER_Z", U32(m_finalClusterZ))
+		.add("FRACTION", UVec3(fractionXY, fractionXY, fractionZ))
+		.add("WORKGROUP_SIZE", UVec2(m_workgroupSize[0], m_workgroupSize[1]));
+
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(consts.get(), variant);
+	m_grProg = variant->getProgram();
+
+	// RT descr
+	m_rtDescr = m_r->create2DRenderTargetDescription(
+		m_volumeSize[0], m_volumeSize[1], LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT, "Fog");
+	m_rtDescr.m_depth = m_volumeSize[2];
+	m_rtDescr.m_type = TextureType::_3D;
+	m_rtDescr.bake();
+
+	return Error::NONE;
+}
+
+void VolumetricFog::run(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+	const RenderingContext& ctx = *m_runCtx.m_ctx;
+
+	cmdb->bindShaderProgram(m_grProg);
+
+	rgraphCtx.bindImage(0, 0, m_runCtx.m_rt, TextureSubresourceInfo());
+
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getVolumetricFog().getRt(), m_r->getLinearSampler());
+
+	struct PushConsts
+	{
+		Vec4 m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1;
+		Vec4 m_fogDiffusePad1;
+		ClustererMagicValues m_clustererMagic;
+	} regs;
+	regs.m_fogScatteringCoeffFogAbsorptionCoeffDensityPad1 =
+		Vec4(m_fogScatteringCoeff, m_fogAbsorptionCoeff, m_fogDensity, 0.0f);
+	regs.m_fogDiffusePad1 = Vec4(m_fogDiffuseColor, 0.0f);
+	regs.m_clustererMagic = ctx.m_clusterBinOut.m_shaderMagicValues;
+
+	cmdb->setPushConstants(&regs, sizeof(regs));
+
+	dispatchPPCompute(cmdb, m_workgroupSize[0], m_workgroupSize[1], m_volumeSize[0], m_volumeSize[1]);
+}
+
+void VolumetricFog::populateRenderGraph(RenderingContext& ctx)
+{
+	m_runCtx.m_ctx = &ctx;
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("Vol fog");
+
+	auto callback = [](RenderPassWorkContext& rgraphCtx) -> void {
+		static_cast<VolumetricFog*>(rgraphCtx.m_userData)->run(rgraphCtx);
+	};
+	pass.setWork(callback, this, 0);
+
+	pass.newDependency({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
+	pass.newDependency({m_r->getVolumetricLightingAccumulation().getRt(), TextureUsageBit::SAMPLED_COMPUTE});
+}
+
+} // end namespace anki

+ 77 - 0
src/anki/renderer/VolumetricFog.h

@@ -0,0 +1,77 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/renderer/RendererObject.h>
+
+namespace anki
+{
+
+/// @addtogroup renderer
+/// @{
+
+/// VolumetricFog effects.
+class VolumetricFog : public RendererObject
+{
+public:
+	void setFogParticleColor(const Vec3& col)
+	{
+		m_fogDiffuseColor = col;
+	}
+
+anki_internal:
+	VolumetricFog(Renderer* r)
+		: RendererObject(r)
+	{
+	}
+
+	~VolumetricFog()
+	{
+	}
+
+	ANKI_USE_RESULT Error init(const ConfigSet& config);
+
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
+
+	RenderTargetHandle getRt() const
+	{
+		return m_runCtx.m_rt;
+	}
+
+	const Array<U32, 3>& getVolumeSize() const
+	{
+		return m_volumeSize;
+	}
+
+private:
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+
+	RenderTargetDescription m_rtDescr;
+
+	U32 m_finalClusterZ = 0;
+
+	Array<U32, 2> m_workgroupSize = {{8, 8}};
+	Array<U32, 3> m_volumeSize;
+
+	Vec3 m_fogDiffuseColor = Vec3(1.0f);
+	F32 m_fogDensity = 0.01f;
+	F32 m_fogScatteringCoeff = 0.1f;
+	F32 m_fogAbsorptionCoeff = 0.2f;
+
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+		const RenderingContext* m_ctx = nullptr;
+	} m_runCtx; ///< Runtime context.
+
+	void run(RenderPassWorkContext& rgraphCtx);
+};
+/// @}
+
+} // end namespace anki