diff --git a/projects/voxelization/resources/shaders/shadowBlur.comp b/projects/voxelization/resources/shaders/shadowBlur.comp
deleted file mode 100644
index 3cf744ab79108c5a9c619524dd8824c39886b357..0000000000000000000000000000000000000000
--- a/projects/voxelization/resources/shaders/shadowBlur.comp
+++ /dev/null
@@ -1,42 +0,0 @@
-#version 450
-#extension GL_GOOGLE_include_directive : enable
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-layout(set=0, binding=0)                    uniform texture2D   srcTexture;
-layout(set=0, binding=1)                    uniform sampler     depthSampler;                
-layout(set=0, binding=2, rgba16)    uniform image2D     outImage;
-
-layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
-
-void main(){
-
-    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){
-        return;
-    }
-    ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
-    
-    int blurRadius  = 9;
-    int minOffset   = -(blurRadius-1) / 2;
-    int maxOffset   = -minOffset;
-    
-    vec2 pixelSize = vec2(1) / textureSize(sampler2D(srcTexture, depthSampler), 0);
-    
-    float wTotal = 0;
-    vec4 moments = vec4(0);
-    
-    float weights1D[4] = { 0.5, 0.25, 0.125, 0.0625 };    // gaussian
-    
-    for(int x = minOffset; x <= maxOffset; x++){
-        for(int y = minOffset; y <= maxOffset; y++){
-            vec2 uv = (coord + ivec2(x, y)) * pixelSize;
-            uv      += 0.5 * pixelSize * sign(vec2(x, y)); // half pixel shift to take advantage of bilinear filtering
-            float w = weights1D[abs(x)] * weights1D[abs(y)];
-            moments += w * texture(sampler2D(srcTexture, depthSampler), uv);
-            wTotal  += w;
-        }
-    }
-    moments /= wTotal;
-    
-    imageStore(outImage, coord, moments);
-}
\ No newline at end of file
diff --git a/projects/voxelization/resources/shaders/shadowBlur.inc b/projects/voxelization/resources/shaders/shadowBlur.inc
new file mode 100644
index 0000000000000000000000000000000000000000..06147415f118dca9badd15813b431a68682ce0b0
--- /dev/null
+++ b/projects/voxelization/resources/shaders/shadowBlur.inc
@@ -0,0 +1,27 @@
+#ifndef SHADOW_BLUR_INC
+#define SHADOW_BLUR_INC
+
+vec4 blurMomentShadowMap1D(ivec2 coord, ivec2 blurDirection, texture2D srcTexture, sampler depthSampler){
+    
+    int blurRadius  = 9;
+    int minOffset   = -(blurRadius-1) / 2;
+    int maxOffset   = -minOffset;
+    
+    vec2 pixelSize = vec2(1) / textureSize(sampler2D(srcTexture, depthSampler), 0);
+    
+    float wTotal = 0;
+    vec4 moments = vec4(0);
+    
+    float weights1D[4] = { 0.5, 0.25, 0.125, 0.0625 };    // gaussian
+    
+    for(int i = minOffset; i <= maxOffset; i++){
+        vec2 uv = (coord + i * blurDirection) * pixelSize;
+        uv      += 0.5 * pixelSize * blurDirection * sign(i); // half pixel shift to take advantage of bilinear filtering
+        float w = weights1D[abs(i)];
+        moments += w * texture(sampler2D(srcTexture, depthSampler), uv);
+        wTotal  += w;
+    }
+    return moments / wTotal;
+}
+
+#endif // #ifndef SHADOW_BLUR_INC
\ No newline at end of file
diff --git a/projects/voxelization/resources/shaders/shadowBlurX.comp b/projects/voxelization/resources/shaders/shadowBlurX.comp
new file mode 100644
index 0000000000000000000000000000000000000000..45b91aad71673347dbf607fecef92463ef1c3c88
--- /dev/null
+++ b/projects/voxelization/resources/shaders/shadowBlurX.comp
@@ -0,0 +1,23 @@
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#include "shadowBlur.inc"
+
+layout(set=0, binding=0)            uniform texture2D   srcTexture;
+layout(set=0, binding=1)            uniform sampler     depthSampler;                
+layout(set=0, binding=2, rgba16)    uniform image2D     outImage;
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+void main(){
+
+    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){
+        return;
+    }
+    ivec2 coord = ivec2(gl_GlobalInvocationID.xy);    
+    vec4 moments = blurMomentShadowMap1D(coord, ivec2(1, 0), srcTexture, depthSampler);
+    
+    imageStore(outImage, coord, moments);
+}
\ No newline at end of file
diff --git a/projects/voxelization/resources/shaders/shadowBlurY.comp b/projects/voxelization/resources/shaders/shadowBlurY.comp
new file mode 100644
index 0000000000000000000000000000000000000000..51d4df054b0d99e54149863a5967143518f61dd2
--- /dev/null
+++ b/projects/voxelization/resources/shaders/shadowBlurY.comp
@@ -0,0 +1,25 @@
+#version 450
+#extension GL_GOOGLE_include_directive : enable
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#include "shadowBlur.inc"
+
+layout(set=0, binding=0)            uniform texture2D   srcTexture;
+layout(set=0, binding=1)            uniform sampler     depthSampler;                
+layout(set=0, binding=2, rgba16)    uniform image2D     outImage;
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+void main(){
+
+    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){
+        return;
+    }
+    ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
+    vec2 pixelSize = vec2(1) / textureSize(sampler2D(srcTexture, depthSampler), 0);
+    
+    vec4 moments = blurMomentShadowMap1D(coord, ivec2(0, 1), srcTexture, depthSampler);
+    
+    imageStore(outImage, coord, moments);
+}
\ No newline at end of file
diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp
index e27895ae030e902a2b41bc1c80a3e2db68dca61f..b50a5f98b55a9b9260c374296a1c24aab597fd72 100644
--- a/projects/voxelization/src/ShadowMapping.cpp
+++ b/projects/voxelization/src/ShadowMapping.cpp
@@ -25,10 +25,20 @@ vkcv::ShaderProgram loadDepthToMomentsShader() {
 	return shader;
 }
 
-vkcv::ShaderProgram loadShadowBlurShader() {
+vkcv::ShaderProgram loadShadowBlurXShader() {
 	vkcv::ShaderProgram shader;
 	vkcv::shader::GLSLCompiler compiler;
-	compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/shadowBlur.comp",
+	compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/shadowBlurX.comp",
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		shader.addShader(shaderStage, path);
+	});
+	return shader;
+}
+
+vkcv::ShaderProgram loadShadowBlurYShader() {
+	vkcv::ShaderProgram shader;
+	vkcv::shader::GLSLCompiler compiler;
+	compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/shadowBlurY.comp",
 		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		shader.addShader(shaderStage, path);
 	});
@@ -174,19 +184,30 @@ ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vert
 	vkcv::DescriptorWrites depthToMomentDescriptorWrites;
 	depthToMomentDescriptorWrites.sampledImageWrites    = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapDepth.getHandle()) };
 	depthToMomentDescriptorWrites.samplerWrites         = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) };
-	depthToMomentDescriptorWrites.storageImageWrites    = { vkcv::StorageImageDescriptorWrite(2, m_shadowMapIntermediate.getHandle()) };
+	depthToMomentDescriptorWrites.storageImageWrites    = { vkcv::StorageImageDescriptorWrite(2, m_shadowMap.getHandle()) };
 	corePtr->writeDescriptorSet(m_depthToMomentsDescriptorSet, depthToMomentDescriptorWrites);
 
-	// shadow blur
-	vkcv::ShaderProgram shadowBlurShader    = loadShadowBlurShader();
-	m_shadowBlurDescriptorSet               = corePtr->createDescriptorSet(shadowBlurShader.getReflectedDescriptors()[0]);
-	m_shadowBlurPipe                        = corePtr->createComputePipeline(shadowBlurShader, { corePtr->getDescriptorSet(m_shadowBlurDescriptorSet).layout });
-
-	vkcv::DescriptorWrites shadowBlurDescriptorWrites;
-	shadowBlurDescriptorWrites.sampledImageWrites   = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapIntermediate.getHandle()) };
-	shadowBlurDescriptorWrites.samplerWrites        = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) };
-	shadowBlurDescriptorWrites.storageImageWrites   = { vkcv::StorageImageDescriptorWrite(2, m_shadowMap.getHandle()) };
-	corePtr->writeDescriptorSet(m_shadowBlurDescriptorSet, shadowBlurDescriptorWrites);
+	// shadow blur X
+	vkcv::ShaderProgram shadowBlurXShader    = loadShadowBlurXShader();
+	m_shadowBlurXDescriptorSet              = corePtr->createDescriptorSet(shadowBlurXShader.getReflectedDescriptors()[0]);
+	m_shadowBlurXPipe                       = corePtr->createComputePipeline(shadowBlurXShader, { corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).layout });
+
+	vkcv::DescriptorWrites shadowBlurXDescriptorWrites;
+	shadowBlurXDescriptorWrites.sampledImageWrites   = { vkcv::SampledImageDescriptorWrite(0, m_shadowMap.getHandle()) };
+	shadowBlurXDescriptorWrites.samplerWrites        = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) };
+	shadowBlurXDescriptorWrites.storageImageWrites   = { vkcv::StorageImageDescriptorWrite(2, m_shadowMapIntermediate.getHandle()) };
+	corePtr->writeDescriptorSet(m_shadowBlurXDescriptorSet, shadowBlurXDescriptorWrites);
+
+	// shadow blur Y
+	vkcv::ShaderProgram shadowBlurYShader = loadShadowBlurYShader();
+	m_shadowBlurYDescriptorSet = corePtr->createDescriptorSet(shadowBlurYShader.getReflectedDescriptors()[0]);
+	m_shadowBlurYPipe = corePtr->createComputePipeline(shadowBlurYShader, { corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).layout });
+
+	vkcv::DescriptorWrites shadowBlurYDescriptorWrites;
+	shadowBlurYDescriptorWrites.sampledImageWrites  = { vkcv::SampledImageDescriptorWrite(0, m_shadowMapIntermediate.getHandle()) };
+	shadowBlurYDescriptorWrites.samplerWrites       = { vkcv::SamplerDescriptorWrite(1, m_shadowSampler) };
+	shadowBlurYDescriptorWrites.storageImageWrites  = { vkcv::StorageImageDescriptorWrite(2, m_shadowMap.getHandle()) };
+	corePtr->writeDescriptorSet(m_shadowBlurYDescriptorSet, shadowBlurYDescriptorWrites);
 }
 
 void ShadowMapping::recordShadowMapRendering(
@@ -245,22 +266,32 @@ void ShadowMapping::recordShadowMapRendering(
 
 	const uint32_t msaaSampleCount = msaaToSampleCount(msaa);
 
-	m_corePtr->prepareImageForStorage(cmdStream, m_shadowMapIntermediate.getHandle());
+	m_corePtr->prepareImageForStorage(cmdStream, m_shadowMap.getHandle());
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
 		m_depthToMomentsPipe,
 		dispatchCount,
 		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_depthToMomentsDescriptorSet).vulkanHandle) },
 		vkcv::PushConstantData((void*)&msaaSampleCount, sizeof(msaaSampleCount)));
+	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
+
+	// blur X
+	m_corePtr->prepareImageForStorage(cmdStream, m_shadowMapIntermediate.getHandle());
+	m_corePtr->recordComputeDispatchToCmdStream(
+		cmdStream,
+		m_shadowBlurXPipe,
+		dispatchCount,
+		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurXDescriptorSet).vulkanHandle) },
+		vkcv::PushConstantData(nullptr, 0));
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle());
 
-	// blur
+	// blur Y
 	m_corePtr->prepareImageForStorage(cmdStream, m_shadowMap.getHandle());
 	m_corePtr->recordComputeDispatchToCmdStream(
 		cmdStream,
-		m_shadowBlurPipe,
+		m_shadowBlurYPipe,
 		dispatchCount,
-		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurDescriptorSet).vulkanHandle) },
+		{ vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_shadowBlurYDescriptorSet).vulkanHandle) },
 		vkcv::PushConstantData(nullptr, 0));
 	m_shadowMap.recordMipChainGeneration(cmdStream);
 	m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle());
diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp
index 2b25220bfc06ba1646003d61ef05c66920e1b1b6..7e06fb58f492837b7a646dfa52c5dd50993df04e 100644
--- a/projects/voxelization/src/ShadowMapping.hpp
+++ b/projects/voxelization/src/ShadowMapping.hpp
@@ -50,6 +50,9 @@ private:
 	vkcv::PipelineHandle        m_depthToMomentsPipe;
 	vkcv::DescriptorSetHandle   m_depthToMomentsDescriptorSet;
 
-	vkcv::PipelineHandle        m_shadowBlurPipe;
-	vkcv::DescriptorSetHandle   m_shadowBlurDescriptorSet;
+	vkcv::PipelineHandle        m_shadowBlurXPipe;
+	vkcv::DescriptorSetHandle   m_shadowBlurXDescriptorSet;
+
+	vkcv::PipelineHandle        m_shadowBlurYPipe;
+	vkcv::DescriptorSetHandle   m_shadowBlurYDescriptorSet;
 };
\ No newline at end of file