diff --git a/projects/voxelization/resources/shaders/brdf.inc b/projects/voxelization/resources/shaders/brdf.inc
index fc97b6013c8c69ecc9f529151dd661ca5f18a328..4cf334eaceedd18815ab928aed38d5f8d3f51c1e 100644
--- a/projects/voxelization/resources/shaders/brdf.inc
+++ b/projects/voxelization/resources/shaders/brdf.inc
@@ -14,14 +14,14 @@ vec3 fresnelSchlick(float cosTheta, vec3 f0){
 float GGXDistribution(float r, float NoH){
     float r2    = r * r;
     float denom = pi * pow(NoH * NoH * (r2 - 1) + 1, 2);
-    return r2 / max(denom, 0.000001);
+    return r2 / max(denom, 0.00001);
 }
 
 float GGXSmithShadowingPart(float r, float cosTheta){
     float nom   = cosTheta * 2;
     float r2    = r * r;
     float denom = cosTheta + sqrt(r2 + (1 - r2) * cosTheta * cosTheta);
-    return nom / max(denom, 0.000001);
+    return nom / max(denom, 0.00001);
 }
 
 float GGXSmithShadowing(float r, float NoV, float NoL){
diff --git a/projects/voxelization/resources/shaders/shader.frag b/projects/voxelization/resources/shaders/shader.frag
index 2b3566912727bc5f48617252bc9240d3993962a4..13a9fa3d03a6d9ffac9d9ce541073378d3cd66c1 100644
--- a/projects/voxelization/resources/shaders/shader.frag
+++ b/projects/voxelization/resources/shaders/shader.frag
@@ -38,12 +38,13 @@ vec3 cookTorrance(vec3 f0, float r, vec3 N, vec3 V, vec3 L){
     
     float NoH = clamp(dot(N, H), 0, 1);
     float NoL = clamp(dot(N, L), 0, 1);
-    float NoV = clamp(dot(N, V), 0, 1);
+    float NoV = clamp(abs(dot(N, V)), 0, 1);    // abs to account for wrong visibility caused by normal mapping
     
-    vec3    F           = fresnelSchlick(NoH, f0);
-    float   D           = GGXDistribution(r, NoH);
-    float   G           = GGXSmithShadowing(r, NoV, NoL);
-    return (F * D * G) / max(4 * NoV * NoL, 0.000001);
+    vec3    F = fresnelSchlick(NoH, f0);
+    float   D = GGXDistribution(r, NoH);
+    float   G = GGXSmithShadowing(r, NoV, NoL);
+    
+    return (F * D * G) / max(4 * NoV * NoL, 0.00001);
 }
 
 float roughnessToConeAngleDegree(float r){
@@ -56,6 +57,17 @@ float interleavedGradientNoise(vec2 uv){
     return fract(magic.z * fract(dot(uv, magic.xy)));
 }
 
+// from: https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
+vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV )
+{
+	const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
+	const vec4 c1 = { 1, 0.0425, 1.04, -0.04 };
+	vec4 r = Roughness * c0 + c1;
+	float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
+	vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
+	return SpecularColor * AB.x + AB.y;
+}
+
 void main()	{
 
     vec3 albedoTexel    = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb;
@@ -74,17 +86,16 @@ void main()	{
     vec3 B      = cross(N_geo, T) * passTangent.w;
     mat3 TBN    = mat3(T, B, N_geo);
     normalTexel = normalTexel * 2 - 1;
-    
-    vec3 N  = TBN * normalTexel;
+
+    vec3 N  = normalize(TBN * normalTexel);
     vec3 L  = lightInfo.L;
     vec3 V  = normalize(cameraPos - passPos);
     
     float NoL = clamp(dot(N, L), 0, 1);    
-    float NoV = clamp(dot(N, V), 0, 1);
-    
-    vec3 sunSpecular = cookTorrance(f0, r, N, V, L);
+    float NoV = clamp(abs(dot(N, V)), 0, 1);
     
-    vec3 sun        = lightInfo.sunStrength * lightInfo.sunColor * NoL;
+    vec3 sunSpecular    = cookTorrance(f0, r, N, V, L);
+    vec3 sun            = lightInfo.sunStrength * lightInfo.sunColor * NoL;
     
     float   noise           = 2 * pi * interleavedGradientNoise(gl_FragCoord.xy);
     vec2    shadowOffset    = 0.05f * vec2(sin(noise), cos(noise)) / textureSize(sampler2D(shadowMap, shadowMapSampler), 0);
@@ -108,7 +119,7 @@ void main()	{
     offsetTraceStart            += R * interleavedGradientNoise(gl_FragCoord.xy) * 0.5;
     vec3 specularTrace          = voxelConeTrace(R, offsetTraceStart, reflectionConeAngle, voxelTexture, voxelSampler, voxelInfo);
     specularTrace               *= clamp(dot(N, R), 0, 1);
-    vec3 reflectionBRDF         = cookTorrance(f0, r, N, V, R);
+    vec3 reflectionBRDF         = EnvBRDFApprox(f0, r, NoV); 
     
 	outColor = 
         (diffuse + sunSpecular) * sun + 
diff --git a/projects/voxelization/resources/shaders/shadowMapping.inc b/projects/voxelization/resources/shaders/shadowMapping.inc
index 4755e7ec9183a8bd7184de8409c1edc959d993ea..c4bcd94e70766dfb4d403076f63007a37d3e868a 100644
--- a/projects/voxelization/resources/shaders/shadowMapping.inc
+++ b/projects/voxelization/resources/shaders/shadowMapping.inc
@@ -70,10 +70,10 @@ float reduceLightBleeding(float shadow, float amount)
 }
 
 float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sampler shadowMapSampler, vec2 offset){
-    vec4 lightPos = lightInfo.lightMatrix * vec4(worldPos, 1);
-    lightPos /= lightPos.w;
-    lightPos.xy = lightPos.xy * 0.5 + 0.5;
-    lightPos.xy += offset;
+    vec4 lightPos   = lightInfo.lightMatrix * vec4(worldPos, 1);
+    lightPos        /= lightPos.w;
+    lightPos.xy     = lightPos.xy * 0.5 + 0.5;
+    lightPos.xy     += offset;
     
     if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){
         return 1;
diff --git a/projects/voxelization/resources/shaders/sky.frag b/projects/voxelization/resources/shaders/sky.frag
new file mode 100644
index 0000000000000000000000000000000000000000..2a3b2ad03e1936641a565b2f3fbd1f19f186ff7a
--- /dev/null
+++ b/projects/voxelization/resources/shaders/sky.frag
@@ -0,0 +1,13 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) out vec3 outColor;
+
+layout( push_constant ) uniform constants{
+    vec3 skyColor;
+    float skyStrength;
+};
+
+void main()	{
+    outColor = skyColor * skyStrength;
+}
\ No newline at end of file
diff --git a/projects/voxelization/resources/shaders/sky.vert b/projects/voxelization/resources/shaders/sky.vert
new file mode 100644
index 0000000000000000000000000000000000000000..686e6f352e9bb1054656f58340a9cfc9b55fcff4
--- /dev/null
+++ b/projects/voxelization/resources/shaders/sky.vert
@@ -0,0 +1,12 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+const vec2 positions[3] = {
+    vec2(-1, -1),
+    vec2(-1, 4),
+    vec2(4, -1)
+};
+
+void main()	{
+	gl_Position = vec4(positions[gl_VertexIndex], 1, 1);
+}
\ No newline at end of file
diff --git a/projects/voxelization/resources/shaders/voxel.inc b/projects/voxelization/resources/shaders/voxel.inc
index a22ea032ccd371f9f131aa007a2b793b2e6091c0..6133ca7cfc52ca77cb70fb8c2cc0e83ef6da4016 100644
--- a/projects/voxelization/resources/shaders/voxel.inc
+++ b/projects/voxelization/resources/shaders/voxel.inc
@@ -149,10 +149,8 @@ vec3 voxelConeTrace(vec3 direction, vec3 startPosition, float coneAngleRadian, t
         color               += (1 - a) * voxelSample.rgb;
         a                   += (1 - a) * voxelSample.a;
         
-        float minStepSize   = 1.5f;
+        float minStepSize   = 1.f;
         d                   += max(coneDiameter, minStepSize);
-        samplePos           = startPosition + d * direction;
-        sampleUV            = worldToVoxelCoordinates(samplePos, voxelInfo);
     }
     return color;
 }
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index fa2b921d9eb88a6f526617e95ec5e5f66bbe808b..daf1211f4d728e7dd180fba06c7164cbd70de30b 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -286,6 +286,52 @@ int main(int argc, const char** argv) {
 		return EXIT_FAILURE;
 	}
 
+	// sky
+	struct SkySettings {
+		glm::vec3   color;
+		float       strength;
+	};
+	SkySettings skySettings;
+	skySettings.color       = glm::vec3(0.15, 0.65, 1);
+	skySettings.strength    = 5;
+
+	const vkcv::AttachmentDescription skyColorAttachment(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::LOAD,
+		colorBufferFormat);
+
+	const vkcv::AttachmentDescription skyDepthAttachments(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::LOAD,
+		depthBufferFormat);
+
+	vkcv::PassConfig skyPassConfig({ skyColorAttachment, skyDepthAttachments }, msaa);
+	vkcv::PassHandle skyPass = core.createPass(skyPassConfig);
+
+	vkcv::ShaderProgram skyShader;
+	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/sky.vert"),
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		skyShader.addShader(shaderStage, path);
+	});
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/sky.frag"),
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		skyShader.addShader(shaderStage, path);
+	});
+
+	vkcv::PipelineConfig skyPipeConfig;
+	skyPipeConfig.m_ShaderProgram       = skyShader;
+	skyPipeConfig.m_Width               = windowWidth;
+	skyPipeConfig.m_Height              = windowHeight;
+	skyPipeConfig.m_PassHandle          = skyPass;
+	skyPipeConfig.m_VertexLayout        = vkcv::VertexLayout({});
+	skyPipeConfig.m_DescriptorLayouts   = {};
+	skyPipeConfig.m_UseDynamicViewport  = true;
+    skyPipeConfig.m_multisampling       = msaa;
+    skyPipeConfig.m_depthWrite          = false;
+
+	vkcv::PipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig);
+
+	// render targets
 	vkcv::ImageHandle depthBuffer           = core.createImage(depthBufferFormat, windowWidth, windowHeight, 1, false, false, false, msaa).getHandle();
 
     const bool colorBufferRequiresStorage   = !usingMsaa;
@@ -526,6 +572,15 @@ int main(int argc, const char** argv) {
 			voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip);
 		}
 
+		// sky
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+			skyPass,
+			skyPipe,
+			vkcv::PushConstantData((void*)&skySettings, sizeof(skySettings)),
+			{ vkcv::DrawcallInfo(vkcv::Mesh({}, nullptr, 3), {}) },
+			renderTargets);
+
 		const uint32_t fullscreenLocalGroupSize = 8;
 		const uint32_t fulsscreenDispatchCount[3] = {
 			static_cast<uint32_t>(glm::ceil(windowWidth  / static_cast<float>(fullscreenLocalGroupSize))),
@@ -581,6 +636,9 @@ int main(int argc, const char** argv) {
 		ImGui::DragFloat("Max shadow distance",     &maxShadowDistance);
 		maxShadowDistance = std::max(maxShadowDistance, 1.f);
 
+		ImGui::ColorEdit3("Sky color",      &skySettings.color.x);
+		ImGui::DragFloat("Sky strength",    &skySettings.strength, 0.1);
+
 		ImGui::Checkbox("Draw voxel visualisation", &renderVoxelVis);
 		ImGui::SliderInt("Visualisation mip",       &voxelVisualisationMip, 0, 7);
 		ImGui::DragFloat("Voxelization extent",     &voxelizationExtent, 1.f, 0.f);
diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp
index df7b7bbcb3fe278622cd160593eb750db00ec7b1..d28ea36c6108306fe603185e9848a72af6455606 100644
--- a/src/vkcv/DrawcallRecording.cpp
+++ b/src/vkcv/DrawcallRecording.cpp
@@ -27,12 +27,14 @@ namespace vkcv {
         // char* cast because void* does not support pointer arithmetic
         const void* drawcallPushConstantData = drawcallPushConstantOffset + (char*)pushConstantData.data;
 
-        cmdBuffer.pushConstants(
-            pipelineLayout,
-            vk::ShaderStageFlagBits::eAll,
-            0,
-            pushConstantData.sizePerDrawcall,
-            drawcallPushConstantData);
+        if (pushConstantData.data && pushConstantData.sizePerDrawcall > 0) {
+            cmdBuffer.pushConstants(
+                pipelineLayout,
+                vk::ShaderStageFlagBits::eAll,
+                0,
+                pushConstantData.sizePerDrawcall,
+                drawcallPushConstantData);
+        }
 
         if (drawcall.mesh.indexBuffer) {
             cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, vk::IndexType::eUint16);	//FIXME: choose proper size
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
index 1b05269a34f90d2df0f218dd6155c2ba85164db7..ca0f9225759cc7f4e16b1ba9596f2b9fd6275464 100644
--- a/src/vkcv/PipelineManager.cpp
+++ b/src/vkcv/PipelineManager.cpp
@@ -221,14 +221,18 @@ namespace vkcv
                 { 1.f,1.f,1.f,1.f }
         );
 
-		const size_t matrixPushConstantSize = config.m_ShaderProgram.getPushConstantSize();
-		const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, matrixPushConstantSize);
+		const size_t pushConstantSize = config.m_ShaderProgram.getPushConstantSize();
+		const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, pushConstantSize);
 
         // pipeline layout
         vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo(
 			{},
 			(config.m_DescriptorLayouts),
 			(pushConstantRange));
+		if (pushConstantSize == 0) {
+			pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
+		}
+
 
         vk::PipelineLayout vkPipelineLayout{};
         if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess)