Browse Source

Adding volumetric fog. Needs more work to look good

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
7c788aa699

+ 11 - 0
shaders/Clusterer.glsl

@@ -39,4 +39,15 @@ uint computeClusterIndexUsingFragCoord(
 		near, clustererMagic, zVSpace, tileCountX, tileCountY, gl_FragCoord.xy);
 		near, clustererMagic, zVSpace, tileCountX, tileCountY, gl_FragCoord.xy);
 }
 }
 
 
+// Compute the Z of the near plane given a cluster idx
+float computeClusterNear(uint k, float near, float clustererMagic)
+{
+	return 1.0 / clustererMagic * pow(float(k), 2.0) - near;
+}
+
+float computeClusterFar(uint k, float near, float clustererMagic)
+{
+	return 1.0 / clustererMagic * pow(float(k + 1u), 2.0) - near;
+}
+
 #endif
 #endif

+ 2 - 2
shaders/FsCommonFrag.glsl

@@ -133,8 +133,8 @@ vec3 computeLightColor(vec3 diffCol)
 	uint clusterIdx = computeClusterIndexUsingCustomFragCoord(u_lightingUniforms.nearFarClustererMagicPad1.x,
 	uint clusterIdx = computeClusterIndexUsingCustomFragCoord(u_lightingUniforms.nearFarClustererMagicPad1.x,
 		u_lightingUniforms.nearFarClustererMagicPad1.z,
 		u_lightingUniforms.nearFarClustererMagicPad1.z,
 		fragPos.z,
 		fragPos.z,
-		u_lightingUniforms.tileCountPad1.x,
-		u_lightingUniforms.tileCountPad1.y,
+		u_lightingUniforms.tileCount.x,
+		u_lightingUniforms.tileCount.y,
 		gl_FragCoord.xy * 2.0);
 		gl_FragCoord.xy * 2.0);
 
 
 	uint idxOffset = u_clusters[clusterIdx];
 	uint idxOffset = u_clusters[clusterIdx];

+ 5 - 5
shaders/FsUpscale.frag.glsl

@@ -14,8 +14,9 @@ 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, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_colorTexNearest;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_colorTexNearest;
 layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_colorTexLinear;
 layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_colorTexLinear;
+layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_volTex;
 #if SSAO_ENABLED
 #if SSAO_ENABLED
-layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_ssaoTex;
+layout(ANKI_TEX_BINDING(0, 5)) uniform sampler2D u_ssaoTex;
 #endif
 #endif
 
 
 layout(ANKI_UBO_BINDING(0, 0)) uniform _u0
 layout(ANKI_UBO_BINDING(0, 0)) uniform _u0
@@ -76,21 +77,20 @@ void main()
 #else
 #else
 	float maxDiffLinear = abs(maxDiff);
 	float maxDiffLinear = abs(maxDiff);
 #endif
 #endif
-	vec3 color;
+	vec3 color = textureLod(u_volTex, in_uv, 0.0).rgb;
 	if(maxDiffLinear < DEPTH_THRESHOLD)
 	if(maxDiffLinear < DEPTH_THRESHOLD)
 	{
 	{
 		// No major discontinuites, sample with bilinear
 		// No major discontinuites, sample with bilinear
-		color = textureLod(u_colorTexLinear, in_uv, 0.0).rgb;
+		color += textureLod(u_colorTexLinear, in_uv, 0.0).rgb;
 	}
 	}
 	else
 	else
 	{
 	{
 		// Some discontinuites, need to use the newUv
 		// Some discontinuites, need to use the newUv
-		color = textureLod(u_colorTexNearest, newUv, 0.0).rgb;
+		color += textureLod(u_colorTexNearest, newUv, 0.0).rgb;
 	}
 	}
 
 
 #if SSAO_ENABLED
 #if SSAO_ENABLED
 	float ssao = texture(u_ssaoTex, in_uv).r;
 	float ssao = texture(u_ssaoTex, in_uv).r;
-	ssao = dither(ssao, 16.0);
 	out_color = vec4(color, ssao);
 	out_color = vec4(color, ssao);
 #else
 #else
 	out_color = vec4(color, 1.0);
 	out_color = vec4(color, 1.0);

+ 5 - 0
shaders/Functions.glsl

@@ -69,4 +69,9 @@ vec4 textureCatmullRom4Samples(sampler2D tex, vec2 uv, vec2 texSize)
 		+ (texture(tex, pos.xw) * w.x + texture(tex, pos.zw) * w.z) * w.w;
 		+ (texture(tex, pos.xw) * w.x + texture(tex, pos.zw) * w.z) * w.w;
 }
 }
 
 
+float rand(vec2 n)
+{
+	return 0.5 + 0.5 * fract(sin(dot(n, vec2(12.9898, 78.233))) * 43758.5453);
+}
+
 #endif
 #endif

+ 1 - 1
shaders/GaussianBlurGeneric.frag.glsl

@@ -89,7 +89,7 @@ void main()
 #endif
 #endif
 
 
 	out_color = COL_TYPE(0.0);
 	out_color = COL_TYPE(0.0);
-	for(uint i = 0; i < STEP_COUNT; ++i)
+	for(uint i = 0u; i < STEP_COUNT; ++i)
 	{
 	{
 		vec2 texCoordOffset = OFFSETS[i] * TEXEL_SIZE;
 		vec2 texCoordOffset = OFFSETS[i] * TEXEL_SIZE;
 		COL_TYPE col =
 		COL_TYPE col =

+ 3 - 1
shaders/Is.frag.glsl

@@ -31,6 +31,8 @@ layout(location = 0) out vec3 out_color;
 
 
 const uint TILE_COUNT = TILE_COUNT_X * TILE_COUNT_Y;
 const uint TILE_COUNT = TILE_COUNT_X * TILE_COUNT_Y;
 
 
+const float SUBSURFACE_MIN = 0.05;
+
 // Return frag pos in view space
 // Return frag pos in view space
 vec3 getFragPosVSpace()
 vec3 getFragPosVSpace()
 {
 {
@@ -146,7 +148,7 @@ void main()
 	normal = gbuffer.normal;
 	normal = gbuffer.normal;
 	roughness = gbuffer.roughness;
 	roughness = gbuffer.roughness;
 	metallic = gbuffer.metallic;
 	metallic = gbuffer.metallic;
-	subsurface = gbuffer.subsurface;
+	subsurface = max(gbuffer.subsurface, SUBSURFACE_MIN);
 	emission = gbuffer.emission;
 	emission = gbuffer.emission;
 
 
 	// Get counts and offsets
 	// Get counts and offsets

+ 1 - 1
shaders/IsFsCommon.glsl

@@ -16,7 +16,7 @@ struct LightingUniforms
 	vec4 nearFarClustererMagicPad1;
 	vec4 nearFarClustererMagicPad1;
 	mat4 viewMat;
 	mat4 viewMat;
 	mat3 invViewRotation;
 	mat3 invViewRotation;
-	uvec4 tileCountPad1;
+	uvec4 tileCount;
 };
 };
 
 
 // Point light
 // Point light

+ 3 - 2
shaders/Pps.frag.glsl

@@ -130,7 +130,7 @@ void main()
 #endif
 #endif
 
 
 #if SHARPEN_ENABLED
 #if SHARPEN_ENABLED
-	out_color = sharpen(u_isRt, uv);
+	out_color = textureCatmullRom4Samples();
 #elif SMAA_ENABLED
 #elif SMAA_ENABLED
 	out_color = SMAANeighborhoodBlendingPS(uv, in_smaaOffset, u_isRt, u_smaaBlendTex).rgb;
 	out_color = SMAANeighborhoodBlendingPS(uv, in_smaaOffset, u_isRt, u_smaaBlendTex).rgb;
 #else
 #else
@@ -148,7 +148,8 @@ void main()
 
 
 #if 0
 #if 0
 	{
 	{
-		out_color = textureLod(u_isRt, uv, 0.0).rgb;
+		out_color = textureCatmullRom4Samples(u_isRt, 
+				uv, vec2(2560. / 2., 1400. / 2.)).rgb;
 	}
 	}
 #endif
 #endif
 
 

+ 119 - 6
shaders/Volumetric.frag.glsl

@@ -5,12 +5,20 @@
 
 
 #include "shaders/Common.glsl"
 #include "shaders/Common.glsl"
 #include "shaders/Functions.glsl"
 #include "shaders/Functions.glsl"
+#include "shaders/Clusterer.glsl"
+
+#define LIGHT_TEX_BINDING 2
+#define LIGHT_UBO_BINDING 0
+#define LIGHT_SS_BINDING 0
+#define LIGHT_SET 0
+#include "shaders/IsFsCommon.glsl"
 
 
 layout(location = 0) in vec2 in_uv;
 layout(location = 0) in vec2 in_uv;
 
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msDepthRt;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msDepthRt;
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_prevResultTex;
 
 
-layout(std140, ANKI_UBO_BINDING(0, 0)) uniform ubo0_
+layout(std140, ANKI_UBO_BINDING(0, 3)) uniform ubo0_
 {
 {
 	vec4 u_linearizePad2;
 	vec4 u_linearizePad2;
 	vec4 u_fogColorFogFactor;
 	vec4 u_fogColorFogFactor;
@@ -18,17 +26,122 @@ layout(std140, ANKI_UBO_BINDING(0, 0)) uniform ubo0_
 
 
 layout(location = 0) out vec3 out_color;
 layout(location = 0) out vec3 out_color;
 
 
-vec3 fog(vec2 uv)
+#define ENABLE_SHADOWS 0
+
+vec3 computeLightColor(vec3 diffCol, vec3 fragPos, uint clusterIdx)
 {
 {
-	float depth = textureLod(u_msDepthRt, uv, 1.0).r;
+	vec3 outColor = vec3(0.0);
 
 
-	float linearDepth = linearizeDepthOptimal(depth, u_linearizePad2.x, u_linearizePad2.y);
+	uint idxOffset = u_clusters[clusterIdx];
+
+	// Skip decals
+	uint count = u_lightIndices[idxOffset];
+	idxOffset += count + 1;
+
+	// Point lights
+	count = u_lightIndices[idxOffset++];
+	while(count-- != 0)
+	{
+		PointLight light = u_pointLights[u_lightIndices[idxOffset++]];
+
+		vec3 diffC = computeDiffuseColor(diffCol, light.diffuseColorShadowmapId.rgb);
+
+		vec3 frag2Light = light.posRadius.xyz - fragPos;
+		float att = computeAttenuationFactor(light.posRadius.w, frag2Light);
+
+		float shadow = 1.0;
+#if ENABLE_SHADOWS
+		float shadowmapLayerIdx = light.diffuseColorShadowmapId.w;
+		if(light.diffuseColorShadowmapId.w < 128.0)
+		{
+			shadow = computeShadowFactorOmni(
+				frag2Light, shadowmapLayerIdx, -1.0 / light.posRadius.w, u_lightingUniforms.viewMat, u_omniMapArr);
+		}
+#endif
+
+		outColor += diffC * (att * shadow);
+	}
+
+	// Spot lights
+	count = u_lightIndices[idxOffset++];
+	while(count-- != 0)
+	{
+		SpotLight light = u_spotLights[u_lightIndices[idxOffset++]];
+
+		vec3 diffC = computeDiffuseColor(diffCol, light.diffuseColorShadowmapId.rgb);
+
+		vec3 frag2Light = light.posRadius.xyz - fragPos;
+		float att = computeAttenuationFactor(light.posRadius.w, frag2Light);
+
+		vec3 l = normalize(frag2Light);
+
+		float spot = computeSpotFactor(l, light.outerCosInnerCos.x, light.outerCosInnerCos.y, light.lightDir.xyz);
+
+		float shadow = 1.0;
+#if ENABLE_SHADOWS
+		float shadowmapLayerIdx = light.diffuseColorShadowmapId.w;
+		if(shadowmapLayerIdx < 128.0)
+		{
+			shadow = computeShadowFactorSpot(light.texProjectionMat, fragPos, shadowmapLayerIdx, 1, u_spotMapArr);
+		}
+#endif
+
+		outColor += diffC * (att * spot * shadow);
+	}
 
 
+	return outColor;
+}
+
+vec3 fog(in float depth)
+{
+	float linearDepth = linearizeDepthOptimal(depth, u_linearizePad2.x, u_linearizePad2.y);
 	float t = linearDepth * u_fogColorFogFactor.w;
 	float t = linearDepth * u_fogColorFogFactor.w;
-	return dither(u_fogColorFogFactor.rgb * t, 4.0);
+	return u_fogColorFogFactor.rgb * t;
 }
 }
 
 
 void main()
 void main()
 {
 {
-	out_color = fog(in_uv);
+	float depth = textureLod(u_msDepthRt, in_uv, 0.0).r;
+	float farZ = u_lightingUniforms.projectionParams.z / (u_lightingUniforms.projectionParams.w + depth);
+
+	vec3 diffCol = fog(depth);
+	vec3 prevCol = texture(u_prevResultTex, in_uv).rgb;
+
+	vec2 ndc = in_uv * 2.0 - 1.0;
+
+	uint i = uint(gl_FragCoord.x * 4.0) >> 6;
+	uint j = uint(gl_FragCoord.y * 4.0) >> 6;
+
+	float randFactor = rand(ndc + u_lightingUniforms.rendererSizeTimePad1.z);
+	// float randFactor = rand(in_uv);
+
+	float kNear = -u_lightingUniforms.nearFarClustererMagicPad1.x;
+	uint k;
+	vec3 newCol = vec3(0.0);
+	for(k = 0u; k < CLUSTER_COUNT.z; ++k)
+	{
+		float kFar = computeClusterFar(
+			k, u_lightingUniforms.nearFarClustererMagicPad1.x, u_lightingUniforms.nearFarClustererMagicPad1.z);
+
+		float zMedian = mix(kNear, kFar, randFactor);
+		if(zMedian < farZ)
+		{
+			break;
+		}
+
+		uint clusterIdx = k * (CLUSTER_COUNT.x * CLUSTER_COUNT.y) + j * CLUSTER_COUNT.x + i;
+
+		vec3 fragPos;
+		fragPos.z = zMedian;
+		fragPos.xy = ndc * u_lightingUniforms.projectionParams.xy * fragPos.z;
+
+		newCol += computeLightColor(diffCol, fragPos, clusterIdx);
+
+		kNear = kFar;
+	}
+
+	newCol = newCol / max(1.0, float(k));
+
+	out_color = mix(newCol, prevCol, 0.666);
+	// out_color = newCol;
 }
 }

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

@@ -50,6 +50,9 @@ const U SSAO_FRACTION = 4;
 /// Bloom size is rendererSize/BLOOM_FRACTION.
 /// Bloom size is rendererSize/BLOOM_FRACTION.
 const U BLOOM_FRACTION = 4;
 const U BLOOM_FRACTION = 4;
 
 
+/// Volumetric size is rendererSize/VOLUMETRIC_FRACTION.
+const U VOLUMETRIC_FRACTION = 4;
+
 /// Computes the 'a' and 'b' numbers for linearizeDepthOptimal
 /// Computes the 'a' and 'b' numbers for linearizeDepthOptimal
 inline void computeLinearizeDepthOptimal(F32 near, F32 far, F32& a, F32& b)
 inline void computeLinearizeDepthOptimal(F32 near, F32 far, F32& a, F32& b)
 {
 {

+ 6 - 5
src/anki/renderer/FsUpscale.cpp

@@ -9,6 +9,7 @@
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Fs.h>
 #include <anki/renderer/Fs.h>
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Ssao.h>
+#include <anki/renderer/Volumetric.h>
 #include <anki/renderer/HalfDepth.h>
 #include <anki/renderer/HalfDepth.h>
 #include <anki/scene/FrustumComponent.h>
 #include <anki/scene/FrustumComponent.h>
 
 
@@ -40,7 +41,9 @@ Error FsUpscale::init(const ConfigSet& config)
 	rcInit.m_textures[3].m_texture = m_r->getFs().getRt();
 	rcInit.m_textures[3].m_texture = m_r->getFs().getRt();
 	rcInit.m_textures[3].m_sampler = gr.newInstance<Sampler>(sinit);
 	rcInit.m_textures[3].m_sampler = gr.newInstance<Sampler>(sinit);
 
 
-	rcInit.m_textures[4].m_texture = m_r->getSsao().getRt();
+	rcInit.m_textures[4].m_texture = m_r->getVolumetric().m_rt;
+
+	rcInit.m_textures[5].m_texture = m_r->getSsao().getRt();
 
 
 	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
 	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
 	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
 	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
@@ -56,11 +59,9 @@ Error FsUpscale::init(const ConfigSet& config)
 		m_r->getHeight() / FS_FRACTION,
 		m_r->getHeight() / FS_FRACTION,
 		1);
 		1);
 
 
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_frag, "shaders/FsUpscale.frag.glsl", pps.toCString(), "r_"));
+	ANKI_CHECK(getResourceManager().loadResourceToCache(m_frag, "shaders/FsUpscale.frag.glsl", pps.toCString(), "r_"));
 
 
-	ANKI_CHECK(
-		getResourceManager().loadResourceToCache(m_vert, "shaders/Quad.vert.glsl", pps.toCString(), "r_"));
+	ANKI_CHECK(getResourceManager().loadResourceToCache(m_vert, "shaders/Quad.vert.glsl", pps.toCString(), "r_"));
 
 
 	// Ppline
 	// Ppline
 	PipelineInitInfo ppinit;
 	PipelineInitInfo ppinit;

+ 1 - 1
src/anki/renderer/Is.cpp

@@ -275,7 +275,7 @@ void Is::updateCommonBlock(RenderingContext& ctx)
 
 
 	blk->m_rendererSizeTimePad1 = Vec4(m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), 0.0);
 	blk->m_rendererSizeTimePad1 = Vec4(m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), 0.0);
 
 
-	blk->m_tileCount = UVec4(m_r->getTileCountXY(), m_r->getTileCount(), 0);
+	blk->m_tileCount = UVec4(m_r->getTileCountXY(), m_lightBin->getClusterer().getClusterCountZ(), m_r->getTileCount());
 }
 }
 
 
 void Is::setPreRunBarriers(RenderingContext& ctx)
 void Is::setPreRunBarriers(RenderingContext& ctx)

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

@@ -47,6 +47,11 @@ anki_internal:
 		return m_rtMipCount;
 		return m_rtMipCount;
 	}
 	}
 
 
+	const LightBin& getLightBin() const
+	{
+		return *m_lightBin;
+	}
+
 private:
 private:
 	static const U COMMON_VARS_LOCATION = 0;
 	static const U COMMON_VARS_LOCATION = 0;
 	static const U P_LIGHTS_LOCATION = 1;
 	static const U P_LIGHTS_LOCATION = 1;

+ 31 - 2
src/anki/renderer/Renderer.cpp

@@ -27,6 +27,8 @@
 #include <anki/renderer/HalfDepth.h>
 #include <anki/renderer/HalfDepth.h>
 #include <anki/renderer/Smaa.h>
 #include <anki/renderer/Smaa.h>
 
 
+#include <cstdarg> // For var args
+
 namespace anki
 namespace anki
 {
 {
 
 
@@ -227,6 +229,7 @@ Error Renderer::render(RenderingContext& ctx)
 
 
 	m_smaa->m_edge.setPreRunBarriers(ctx);
 	m_smaa->m_edge.setPreRunBarriers(ctx);
 	m_smaa->m_weights.setPreRunBarriers(ctx);
 	m_smaa->m_weights.setPreRunBarriers(ctx);
+	m_vol->setPreRunBarriers(ctx);
 
 
 	// SM
 	// SM
 	m_sm->run(ctx);
 	m_sm->run(ctx);
@@ -250,9 +253,11 @@ Error Renderer::render(RenderingContext& ctx)
 	// Batch FS & SSAO
 	// Batch FS & SSAO
 	m_fs->run(ctx);
 	m_fs->run(ctx);
 	m_ssao->run(ctx);
 	m_ssao->run(ctx);
+	m_vol->run(ctx);
 
 
 	m_ssao->setPostRunBarriers(ctx);
 	m_ssao->setPostRunBarriers(ctx);
 	m_fs->setPostRunBarriers(ctx);
 	m_fs->setPostRunBarriers(ctx);
+	m_vol->setPostRunBarriers(ctx);
 
 
 	m_fsUpscale->run(ctx);
 	m_fsUpscale->run(ctx);
 
 
@@ -357,6 +362,18 @@ void Renderer::createRenderTarget(
 	rt = m_gr->newInstance<Texture>(init);
 	rt = m_gr->newInstance<Texture>(init);
 }
 }
 
 
+void Renderer::clearRenderTarget(TexturePtr rt, const ClearValue& clear, TextureUsageBit transferTo)
+{
+	TextureSurfaceInfo surf = {0, 0, 0, 0};
+	CommandBufferInitInfo cinit;
+	cinit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
+	CommandBufferPtr cmdb = m_gr->newInstance<CommandBuffer>(cinit);
+	cmdb->setTextureSurfaceBarrier(rt, TextureUsageBit::NONE, TextureUsageBit::CLEAR, surf);
+	cmdb->clearTextureSurface(rt, surf, clear);
+	cmdb->setTextureSurfaceBarrier(rt, TextureUsageBit::CLEAR, transferTo, surf);
+	cmdb->flush();
+}
+
 void Renderer::createDrawQuadPipeline(ShaderPtr frag, const ColorStateInfo& colorState, PipelinePtr& ppline)
 void Renderer::createDrawQuadPipeline(ShaderPtr frag, const ColorStateInfo& colorState, PipelinePtr& ppline)
 {
 {
 	PipelineInitInfo init;
 	PipelineInitInfo init;
@@ -402,7 +419,6 @@ Error Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId,
 	if(ctx.m_fs.m_lastThreadWithWork == threadId)
 	if(ctx.m_fs.m_lastThreadWithWork == threadId)
 	{
 	{
 		m_lf->run(ctx, ctx.m_fs.m_commandBuffers[threadId]);
 		m_lf->run(ctx, ctx.m_fs.m_commandBuffers[threadId]);
-		m_vol->run(ctx, ctx.m_fs.m_commandBuffers[threadId]);
 	}
 	}
 	else if(threadId == threadCount - 1 && ctx.m_fs.m_lastThreadWithWork == MAX_U32)
 	else if(threadId == threadCount - 1 && ctx.m_fs.m_lastThreadWithWork == MAX_U32)
 	{
 	{
@@ -417,7 +433,6 @@ Error Renderer::buildCommandBuffersInternal(RenderingContext& ctx, U32 threadId,
 		cmdb->setPolygonOffset(0.0, 0.0);
 		cmdb->setPolygonOffset(0.0, 0.0);
 
 
 		m_lf->run(ctx, cmdb);
 		m_lf->run(ctx, cmdb);
-		m_vol->run(ctx, cmdb);
 
 
 		ctx.m_fs.m_commandBuffers[threadId] = cmdb;
 		ctx.m_fs.m_commandBuffers[threadId] = cmdb;
 	}
 	}
@@ -488,4 +503,18 @@ Error Renderer::buildCommandBuffers(RenderingContext& ctx)
 	return err;
 	return err;
 }
 }
 
 
+Error Renderer::createShader(CString fname, ShaderResourcePtr& shader, CString fmt, ...)
+{
+	Array<char, 512> buffer;
+	va_list args;
+
+	va_start(args, fmt);
+	I len = std::vsnprintf(&buffer[0], sizeof(buffer), &fmt[0], args);
+	va_end(args);
+	ANKI_ASSERT(len > 0 && len < I(sizeof(buffer) - 1));
+	(void)len;
+
+	return m_resources->loadResourceToCache(shader, fname, &buffer[0], "r_");
+}
+
 } // end namespace anki
 } // end namespace anki

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

@@ -306,6 +306,10 @@ anki_internal:
 		U mipsCount,
 		U mipsCount,
 		TexturePtr& rt);
 		TexturePtr& rt);
 
 
+	void clearRenderTarget(TexturePtr rt, const ClearValue& clear, TextureUsageBit transferTo);
+
+	ANKI_USE_RESULT Error createShader(CString fname, ShaderResourcePtr& shader, CString fmt, ...);
+
 	GrManager& getGrManager()
 	GrManager& getGrManager()
 	{
 	{
 		return *m_gr;
 		return *m_gr;

+ 143 - 27
src/anki/renderer/Volumetric.cpp

@@ -6,6 +6,9 @@
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/HalfDepth.h>
 #include <anki/renderer/HalfDepth.h>
+#include <anki/renderer/Sm.h>
+#include <anki/renderer/Is.h>
+#include <anki/renderer/LightBin.h>
 #include <anki/scene/FrustumComponent.h>
 #include <anki/scene/FrustumComponent.h>
 
 
 namespace anki
 namespace anki
@@ -17,53 +20,166 @@ Volumetric::~Volumetric()
 
 
 Error Volumetric::init(const ConfigSet& config)
 Error Volumetric::init(const ConfigSet& config)
 {
 {
-	// Create frag shader
-	ANKI_CHECK(getResourceManager().loadResource("shaders/Volumetric.frag.glsl", m_frag));
-
-	// Create ppline
-	PipelineInitInfo init;
-	init.m_inputAssembler.m_topology = PrimitiveTopology::TRIANGLE_STRIP;
-	init.m_depthStencil.m_depthWriteEnabled = false;
-	init.m_depthStencil.m_depthCompareFunction = CompareOperation::ALWAYS;
-	init.m_depthStencil.m_format = MS_DEPTH_ATTACHMENT_PIXEL_FORMAT;
-	init.m_color.m_attachmentCount = 1;
-	init.m_color.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
-	init.m_color.m_attachments[0].m_srcBlendMethod = BlendMethod::ONE;
-	init.m_color.m_attachments[0].m_dstBlendMethod = BlendMethod::ONE;
-	init.m_shaders[ShaderType::VERTEX] = m_r->getDrawQuadVertexShader();
-	init.m_shaders[ShaderType::FRAGMENT] = m_frag->getGrShader();
-
-	m_ppline = getGrManager().newInstance<Pipeline>(init);
-
-	// Create the resource group
+	U width = m_r->getWidth() / VOLUMETRIC_FRACTION;
+	U height = m_r->getHeight() / VOLUMETRIC_FRACTION;
+
+	// Create RTs
+	m_r->createRenderTarget(width,
+		height,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		SamplingFilter::LINEAR,
+		1,
+		m_rt);
+	m_r->createRenderTarget(width,
+		height,
+		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::CLEAR,
+		SamplingFilter::LINEAR,
+		1,
+		m_tmpRt);
+
+	m_r->clearRenderTarget(m_tmpRt, ClearValue(), TextureUsageBit::SAMPLED_FRAGMENT);
+
+	// Create shaders
+	ANKI_CHECK(m_r->createShader("shaders/Volumetric.frag.glsl",
+		m_frag,
+		"#define RPASS_SIZE uvec2(%uu, %uu)\n"
+		"#define CLUSTER_COUNT uvec3(%uu, %uu, %uu)\n",
+		width,
+		height,
+		m_r->getIs().getLightBin().getClusterer().getClusterCountX(),
+		m_r->getIs().getLightBin().getClusterer().getClusterCountY(),
+		m_r->getIs().getLightBin().getClusterer().getClusterCountZ()));
+
+	ANKI_CHECK(m_r->createShader("shaders/GaussianBlurGeneric.frag.glsl",
+		m_hblurFrag,
+		"#define HPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(width),
+		F32(height)));
+
+	ANKI_CHECK(m_r->createShader("shaders/GaussianBlurGeneric.frag.glsl",
+		m_vblurFrag,
+		"#define VPASS\n"
+		"#define COL_RGB\n"
+		"#define TEXTURE_SIZE vec2(%f, %f)\n"
+		"#define KERNEL_SIZE 11\n",
+		F32(width),
+		F32(height)));
+
+	// Create pplines
+	ColorStateInfo state;
+	state.m_attachmentCount = 1;
+	state.m_attachments[0].m_format = IS_COLOR_ATTACHMENT_PIXEL_FORMAT;
+
+	m_r->createDrawQuadPipeline(m_frag->getGrShader(), state, m_ppline);
+	m_r->createDrawQuadPipeline(m_hblurFrag->getGrShader(), state, m_hblurPpline);
+	m_r->createDrawQuadPipeline(m_vblurFrag->getGrShader(), state, m_vblurPpline);
+
+	// Create the resource groups
 	ResourceGroupInitInfo rcInit;
 	ResourceGroupInitInfo rcInit;
 	rcInit.m_textures[0].m_texture = m_r->getHalfDepth().m_depthRt;
 	rcInit.m_textures[0].m_texture = m_r->getHalfDepth().m_depthRt;
 	rcInit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
 	rcInit.m_textures[0].m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ;
+	rcInit.m_textures[1].m_texture = m_tmpRt;
+	rcInit.m_textures[2].m_texture = m_r->getSm().getSpotTextureArray();
+	rcInit.m_textures[3].m_texture = m_r->getSm().getOmniTextureArray();
 	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
 	rcInit.m_uniformBuffers[0].m_uploadedMemory = true;
 	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
 	rcInit.m_uniformBuffers[0].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
+	rcInit.m_uniformBuffers[1].m_uploadedMemory = true;
+	rcInit.m_uniformBuffers[1].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
+	rcInit.m_uniformBuffers[2].m_uploadedMemory = true;
+	rcInit.m_uniformBuffers[2].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
+	rcInit.m_uniformBuffers[3].m_uploadedMemory = true;
+	rcInit.m_uniformBuffers[3].m_usage = BufferUsageBit::UNIFORM_FRAGMENT;
+	rcInit.m_storageBuffers[0].m_uploadedMemory = true;
+	rcInit.m_storageBuffers[0].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
+	rcInit.m_storageBuffers[1].m_uploadedMemory = true;
+	rcInit.m_storageBuffers[1].m_usage = BufferUsageBit::STORAGE_FRAGMENT_READ;
+	m_rc = getGrManager().newInstance<ResourceGroup>(rcInit);
+
+	rcInit = ResourceGroupInitInfo();
+	rcInit.m_textures[0].m_texture = m_rt;
+	m_hblurRc = getGrManager().newInstance<ResourceGroup>(rcInit);
 
 
-	m_rcGroup = getGrManager().newInstance<ResourceGroup>(rcInit);
+	rcInit.m_textures[0].m_texture = m_tmpRt;
+	m_vblurRc = getGrManager().newInstance<ResourceGroup>(rcInit);
+
+	// Create FBs
+	FramebufferInitInfo fbInit;
+	fbInit.m_colorAttachmentCount = 1;
+	fbInit.m_colorAttachments[0].m_texture = m_tmpRt;
+	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+	fbInit.m_colorAttachments[0].m_usageInsideRenderPass = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE;
+	m_hblurFb = getGrManager().newInstance<Framebuffer>(fbInit);
+
+	fbInit.m_colorAttachments[0].m_texture = m_rt;
+	m_vblurFb = getGrManager().newInstance<Framebuffer>(fbInit);
 
 
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
-void Volumetric::run(RenderingContext& ctx, CommandBufferPtr cmdb)
+void Volumetric::setPreRunBarriers(RenderingContext& ctx)
 {
 {
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(
+		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+}
+
+void Volumetric::setPostRunBarriers(RenderingContext& ctx)
+{
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+}
+
+void Volumetric::run(RenderingContext& ctx)
+{
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
 	const Frustum& frc = ctx.m_frustumComponent->getFrustum();
 
 
 	// Update uniforms
 	// Update uniforms
-	TransientMemoryInfo dyn;
+	TransientMemoryInfo dyn = ctx.m_is.m_dynBufferInfo;
 	Vec4* uniforms = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
 	Vec4* uniforms = static_cast<Vec4*>(getGrManager().allocateFrameTransientMemory(
-		sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[0]));
-
+		sizeof(Vec4) * 2, BufferUsageBit::UNIFORM_ALL, dyn.m_uniformBuffers[3]));
 	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), uniforms[0].x(), uniforms[0].y());
 	computeLinearizeDepthOptimal(frc.getNear(), frc.getFar(), uniforms[0].x(), uniforms[0].y());
-
 	uniforms[1] = Vec4(m_fogColor, m_fogFactor);
 	uniforms[1] = Vec4(m_fogColor, m_fogFactor);
 
 
-	// Draw
+	// pass 0
+	cmdb->setViewport(0, 0, m_r->getWidth() / VOLUMETRIC_FRACTION, m_r->getHeight() / VOLUMETRIC_FRACTION);
+	cmdb->beginRenderPass(m_vblurFb);
 	cmdb->bindPipeline(m_ppline);
 	cmdb->bindPipeline(m_ppline);
-	cmdb->bindResourceGroup(m_rcGroup, 0, &dyn);
+	cmdb->bindResourceGroup(m_rc, 0, &dyn);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+
+	// hpass
+	cmdb->setTextureSurfaceBarrier(m_rt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->setTextureSurfaceBarrier(
+		m_tmpRt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->beginRenderPass(m_hblurFb);
+	cmdb->bindPipeline(m_hblurPpline);
+	cmdb->bindResourceGroup(m_hblurRc, 0, nullptr);
+	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
+
+	// vpass
+	cmdb->setTextureSurfaceBarrier(m_tmpRt,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+		TextureUsageBit::SAMPLED_FRAGMENT,
+		TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->setTextureSurfaceBarrier(
+		m_rt, TextureUsageBit::NONE, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, 0, 0));
+	cmdb->beginRenderPass(m_vblurFb);
+	cmdb->bindPipeline(m_vblurPpline);
+	cmdb->bindResourceGroup(m_vblurRc, 0, nullptr);
 	m_r->drawQuad(cmdb);
 	m_r->drawQuad(cmdb);
+	cmdb->endRenderPass();
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 19 - 2
src/anki/renderer/Volumetric.h

@@ -24,6 +24,8 @@ public:
 	}
 	}
 
 
 anki_internal:
 anki_internal:
+	TexturePtr m_rt;
+
 	Volumetric(Renderer* r)
 	Volumetric(Renderer* r)
 		: RenderingPass(r)
 		: RenderingPass(r)
 	{
 	{
@@ -33,12 +35,27 @@ anki_internal:
 
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 	ANKI_USE_RESULT Error init(const ConfigSet& config);
 
 
-	void run(RenderingContext& ctx, CommandBufferPtr cmdb);
+	void setPreRunBarriers(RenderingContext& ctx);
+	void run(RenderingContext& ctx);
+	void setPostRunBarriers(RenderingContext& ctx);
 
 
 private:
 private:
+	TexturePtr m_tmpRt;
+
+	ResourceGroupPtr m_rc;
+	ResourceGroupPtr m_hblurRc;
+	ResourceGroupPtr m_vblurRc;
+
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_frag;
-	ResourceGroupPtr m_rcGroup;
+	ShaderResourcePtr m_vblurFrag;
+	ShaderResourcePtr m_hblurFrag;
+
 	PipelinePtr m_ppline;
 	PipelinePtr m_ppline;
+	PipelinePtr m_hblurPpline;
+	PipelinePtr m_vblurPpline;
+
+	FramebufferPtr m_vblurFb;
+	FramebufferPtr m_hblurFb;
 
 
 	Vec3 m_fogColor = Vec3(1.0);
 	Vec3 m_fogColor = Vec3(1.0);
 	F32 m_fogFactor = 1.0;
 	F32 m_fogFactor = 1.0;