diff --git a/projects/bloom/CMakeLists.txt b/projects/bloom/CMakeLists.txt
index bcd8827f98b479d7444eaa9f0628e8de569f7a8d..cdb30af05794ed4a1ef838b8d7c8071e1d943aba 100644
--- a/projects/bloom/CMakeLists.txt
+++ b/projects/bloom/CMakeLists.txt
@@ -22,7 +22,7 @@ if(MSVC)
 endif()
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(bloom SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include})
+target_include_directories(bloom SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include})
 
 # linking with libraries from all dependencies and the VkCV framework
-target_link_libraries(bloom vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera)
+target_link_libraries(bloom vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler)
diff --git a/projects/bloom/resources/Sponza/Sponza.bin b/projects/bloom/resources/Sponza/Sponza.bin
new file mode 100644
index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a
--- /dev/null
+++ b/projects/bloom/resources/Sponza/Sponza.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68
+size 6347696
diff --git a/projects/bloom/resources/Sponza/Sponza.gltf b/projects/bloom/resources/Sponza/Sponza.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230
--- /dev/null
+++ b/projects/bloom/resources/Sponza/Sponza.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787
+size 713962
diff --git a/projects/bloom/resources/Sponza/background.png b/projects/bloom/resources/Sponza/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327
--- /dev/null
+++ b/projects/bloom/resources/Sponza/background.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686
+size 1417666
diff --git a/projects/bloom/resources/Sponza/chain_texture.png b/projects/bloom/resources/Sponza/chain_texture.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5
--- /dev/null
+++ b/projects/bloom/resources/Sponza/chain_texture.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4
+size 490895
diff --git a/projects/bloom/resources/Sponza/lion.png b/projects/bloom/resources/Sponza/lion.png
new file mode 100644
index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b
--- /dev/null
+++ b/projects/bloom/resources/Sponza/lion.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652
+size 2088728
diff --git a/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df
--- /dev/null
+++ b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851
+size 1928829
diff --git a/projects/bloom/resources/Sponza/sponza_arch_diff.png b/projects/bloom/resources/Sponza/sponza_arch_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_arch_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57
+size 1596151
diff --git a/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6
+size 1872074
diff --git a/projects/bloom/resources/Sponza/sponza_column_a_diff.png b/projects/bloom/resources/Sponza/sponza_column_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_column_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826
+size 1840992
diff --git a/projects/bloom/resources/Sponza/sponza_column_b_diff.png b/projects/bloom/resources/Sponza/sponza_column_b_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_column_b_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b
+size 2170433
diff --git a/projects/bloom/resources/Sponza/sponza_column_c_diff.png b/projects/bloom/resources/Sponza/sponza_column_c_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_column_c_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735
+size 2066950
diff --git a/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb
+size 9510905
diff --git a/projects/bloom/resources/Sponza/sponza_curtain_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_curtain_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8
+size 9189263
diff --git a/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476
+size 8785458
diff --git a/projects/bloom/resources/Sponza/sponza_details_diff.png b/projects/bloom/resources/Sponza/sponza_details_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_details_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c
+size 1522643
diff --git a/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab
+size 2229440
diff --git a/projects/bloom/resources/Sponza/sponza_fabric_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_fabric_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287
+size 2267382
diff --git a/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993
+size 2258220
diff --git a/projects/bloom/resources/Sponza/sponza_flagpole_diff.png b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438
+size 1425673
diff --git a/projects/bloom/resources/Sponza/sponza_floor_a_diff.png b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab
+size 1996838
diff --git a/projects/bloom/resources/Sponza/sponza_roof_diff.png b/projects/bloom/resources/Sponza/sponza_roof_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_roof_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b
+size 2320533
diff --git a/projects/bloom/resources/Sponza/sponza_thorn_diff.png b/projects/bloom/resources/Sponza/sponza_thorn_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a
--- /dev/null
+++ b/projects/bloom/resources/Sponza/sponza_thorn_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e
+size 635001
diff --git a/projects/bloom/resources/Sponza/vase_dif.png b/projects/bloom/resources/Sponza/vase_dif.png
new file mode 100644
index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56
--- /dev/null
+++ b/projects/bloom/resources/Sponza/vase_dif.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9
+size 1842101
diff --git a/projects/bloom/resources/Sponza/vase_hanging.png b/projects/bloom/resources/Sponza/vase_hanging.png
new file mode 100644
index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378
--- /dev/null
+++ b/projects/bloom/resources/Sponza/vase_hanging.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31
+size 1432804
diff --git a/projects/bloom/resources/Sponza/vase_plant.png b/projects/bloom/resources/Sponza/vase_plant.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9
--- /dev/null
+++ b/projects/bloom/resources/Sponza/vase_plant.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e
+size 998089
diff --git a/projects/bloom/resources/Sponza/vase_round.png b/projects/bloom/resources/Sponza/vase_round.png
new file mode 100644
index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b
--- /dev/null
+++ b/projects/bloom/resources/Sponza/vase_round.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a
+size 1871399
diff --git a/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg b/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg
deleted file mode 100644
index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
-size 1192476
diff --git a/projects/bloom/resources/cube/cube.bin b/projects/bloom/resources/cube/cube.bin
deleted file mode 100644
index 3303cd8635848bee18e10ab8754d5e4e7218db92..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/cube.bin
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4
-size 840
diff --git a/projects/bloom/resources/cube/cube.blend b/projects/bloom/resources/cube/cube.blend
deleted file mode 100644
index 62ccb2c742094bcfb5ed194ab905bffae86bfd65..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/cube.blend
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09
-size 774920
diff --git a/projects/bloom/resources/cube/cube.blend1 b/projects/bloom/resources/cube/cube.blend1
deleted file mode 100644
index 13f21dcca218d7bc7a07a8a9682b5e1d9e607736..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/cube.blend1
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b
-size 774920
diff --git a/projects/bloom/resources/cube/cube.glb b/projects/bloom/resources/cube/cube.glb
deleted file mode 100644
index 66a42c65e71dcf375e04cc378256024dd3c7834d..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/cube.glb
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897
-size 1194508
diff --git a/projects/bloom/resources/cube/cube.gltf b/projects/bloom/resources/cube/cube.gltf
deleted file mode 100644
index 428176144843dd06c78fe1d11a6392a0ea02b22d..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/cube/cube.gltf
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac
-size 2823
diff --git a/projects/bloom/resources/shaders/blur.comp b/projects/bloom/resources/shaders/blur.comp
index d7fcfb3c21f2f545c90f65d3a22e66362ab6dab9..9fd2ccbf933abd34457a4685149dfae7c2f611a4 100644
--- a/projects/bloom/resources/shaders/blur.comp
+++ b/projects/bloom/resources/shaders/blur.comp
@@ -1,17 +1,37 @@
-# version 450
-layout(local_size_x = 8, local_size_y = 8) in;
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(set=0, binding=0) uniform texture2D                          inImage;
+layout(set=0, binding=1) uniform sampler                            inImageSampler;
+layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D  outImage;
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
 
-layout(set=0, binding=0) uniform texture2D  offscreenAttachment;
-layout(set=0, binding=1) uniform sampler    offscreenSampler;
-layout(r11f_g11f_b10f, set=0, binding=2) uniform writeonly image2D  blurAttachment;
 
 void main()
 {
-    ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
-    vec2 uv = vec2(pixel_coords) / imageSize(blurAttachment);
+    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){
+        return;
+    }
+
+    const int kernel_size = 10;
+    const float kernel_weight = (2 * kernel_size + 1) * (2 * kernel_size + 1);
+
+    ivec2 pixel_coord   = ivec2(gl_GlobalInvocationID.xy);
+    vec2  pixel_size    = vec2(1.0f) / textureSize(sampler2D(inImage, inImageSampler), 0);
+    vec2  UV            = pixel_coord.xy * pixel_size;
 
-    // TODO: BLUR
+    vec4 sampled_color = vec4(0.0f);
 
+    for(int i = -kernel_size; i <= kernel_size; i++)
+    {
+        for(int j = -kernel_size; j <= kernel_size; j++)
+        {
+            vec2 sample_coord = UV + vec2(j, i) * pixel_size + 0.5f * pixel_size * sign(vec2(j, i));
+            sampled_color.rgb += texture(sampler2D(inImage, inImageSampler), sample_coord).rgb;
+        }
+    }
+    sampled_color /= kernel_weight;
 
-    imageStore(blurAttachment, pixel_coords, vec4(1.0, 0.0 ,0.0, 0.0));
+    imageStore(outImage, pixel_coord, sampled_color);
 }
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/blur_comp.spv b/projects/bloom/resources/shaders/blur_comp.spv
deleted file mode 100644
index 565eab96c0b2c554556077dee67d21ed92f42764..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/blur_comp.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/comp.spv b/projects/bloom/resources/shaders/comp.spv
new file mode 100644
index 0000000000000000000000000000000000000000..e0112a50deceb3b818434636e194d4a2f169184b
Binary files /dev/null and b/projects/bloom/resources/shaders/comp.spv differ
diff --git a/projects/bloom/resources/shaders/compile.bat b/projects/bloom/resources/shaders/compile.bat
deleted file mode 100644
index 5fecf915079df6cf230254ef2c1755c953d826b1..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/shaders/compile.bat
+++ /dev/null
@@ -1,11 +0,0 @@
-%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv
-%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv
-
-%VULKAN_SDK%\Bin32\glslc.exe shadow.vert -o shadow_vert.spv
-%VULKAN_SDK%\Bin32\glslc.exe shadow.frag -o shadow_frag.spv
-
-%VULKAN_SDK%\Bin32\glslc.exe quad.vert -o quad_vert.spv
-%VULKAN_SDK%\Bin32\glslc.exe quad.frag -o quad_frag.spv
-
-%VULKAN_SDK%\Bin32\glslc.exe blur.comp   -o blur_comp.spv
-pause
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/compositeBloom.comp b/projects/bloom/resources/shaders/compositeBloom.comp
new file mode 100644
index 0000000000000000000000000000000000000000..d01e758254de37d315d678fe32292260da7579db
--- /dev/null
+++ b/projects/bloom/resources/shaders/compositeBloom.comp
@@ -0,0 +1,29 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(set=0, binding=0) uniform texture2D                          blurImage;
+layout(set=0, binding=1) uniform sampler                            linearSampler;
+layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D            colorBuffer;
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+
+void main()
+{
+    if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){
+        return;
+    }
+
+    ivec2 pixel_coord   = ivec2(gl_GlobalInvocationID.xy);
+    vec2  pixel_size    = vec2(1.0f) / textureSize(sampler2D(blurImage, linearSampler), 0);
+    vec2  UV            = pixel_coord.xy * pixel_size;
+
+    vec4 composite_color = vec4(0.0f);
+
+    vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb;
+    vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb;
+
+    composite_color.rgb = mix(main_color, blur_color, 0.1f);
+
+    imageStore(colorBuffer, pixel_coord, composite_color);
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/frag.spv b/projects/bloom/resources/shaders/frag.spv
deleted file mode 100644
index ba47f91cdfcd1ecd824614f23d424b0a510ac1c7..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/frag.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/gammaCorrection.comp b/projects/bloom/resources/shaders/gammaCorrection.comp
new file mode 100644
index 0000000000000000000000000000000000000000..f89ad167c846cca8e80f69d33eda83bd6ed00d46
--- /dev/null
+++ b/projects/bloom/resources/shaders/gammaCorrection.comp
@@ -0,0 +1,20 @@
+#version 440
+
+layout(set=0, binding=0, r11f_g11f_b10f)    uniform image2D inImage;
+layout(set=0, binding=1, rgba8)             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(inImage)))){
+        return;
+    }
+    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
+    vec3 linearColor = imageLoad(inImage, uv).rgb;
+    // cheap Reinhard tone mapping
+    linearColor = linearColor/(linearColor + 1.0f);
+    vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f));
+    imageStore(outImage, uv, vec4(gammaCorrected, 0.f));
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/perMeshResources.inc b/projects/bloom/resources/shaders/perMeshResources.inc
new file mode 100644
index 0000000000000000000000000000000000000000..95e4fb7c27009965659d14a9c72acfec950c37e3
--- /dev/null
+++ b/projects/bloom/resources/shaders/perMeshResources.inc
@@ -0,0 +1,2 @@
+layout(set=1, binding=0) uniform texture2D  albedoTexture;
+layout(set=1, binding=1) uniform sampler    textureSampler;
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/quad.frag b/projects/bloom/resources/shaders/quad.frag
deleted file mode 100644
index 14bacc9508c783b9d336c1ebc720cdafd5e044b7..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/shaders/quad.frag
+++ /dev/null
@@ -1,15 +0,0 @@
-#version 450
-#extension GL_ARB_separate_shader_objects : enable
-
-layout(location = 0) in vec2 inUV;
-
-layout(location = 0) out vec3 outColor;
-
-layout(set=0, binding=0) uniform texture2D  renderAttachment;
-layout(set=0, binding=1) uniform texture2D  blurAttachment;
-layout(set=0, binding=2) uniform sampler    samp;
-
-void main()
-{
-    outColor = texture(sampler2D(renderAttachment, samp), inUV).rgb;
-}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/quad.vert b/projects/bloom/resources/shaders/quad.vert
deleted file mode 100644
index a5d520084e8afd1110f6b4535754a0988dfc7cee..0000000000000000000000000000000000000000
--- a/projects/bloom/resources/shaders/quad.vert
+++ /dev/null
@@ -1,12 +0,0 @@
-#version 450
-#extension GL_ARB_separate_shader_objects : enable
-
-layout(location = 0) in vec3 inPosition;
-
-layout(location = 0) out vec2 outUV;
-
-void main()
-{
-    outUV = inPosition.xy;
-    gl_Position = vec4(inPosition, 1.0);
-}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/quad_frag.spv b/projects/bloom/resources/shaders/quad_frag.spv
deleted file mode 100644
index 86f9763845401abb1f01879b58b234ffb5904efc..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/quad_frag.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/quad_vert.spv b/projects/bloom/resources/shaders/quad_vert.spv
deleted file mode 100644
index c9c7a116497ce2098b9854b538d07fd99a77e383..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/quad_vert.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/shader.frag b/projects/bloom/resources/shaders/shader.frag
index 1c97a68808af11765fa6bf8a535218a8a2b14a77..3e95b4508f112c1ed9aa4a7050a98fa789dccd09 100644
--- a/projects/bloom/resources/shaders/shader.frag
+++ b/projects/bloom/resources/shaders/shader.frag
@@ -1,5 +1,8 @@
 #version 450
 #extension GL_ARB_separate_shader_objects : enable
+#extension GL_GOOGLE_include_directive : enable
+
+#include "perMeshResources.inc"
 
 layout(location = 0) in vec3 passNormal;
 layout(location = 1) in vec2 passUV;
@@ -7,14 +10,12 @@ layout(location = 2) in vec3 passPos;
 
 layout(location = 0) out vec3 outColor;
 
-layout(set=0, binding=0) uniform texture2D  meshTexture;
-layout(set=0, binding=1) uniform sampler    textureSampler;
-layout(set=0, binding=2) uniform sunBuffer {
+layout(set=0, binding=0) uniform sunBuffer {
     vec3 L; float padding;
     mat4 lightMatrix;
 };
-layout(set=0, binding=3) uniform texture2D  shadowMap;
-layout(set=0, binding=4) uniform sampler    shadowMapSampler;
+layout(set=0, binding=1) uniform texture2D  shadowMap;
+layout(set=0, binding=2) uniform sampler    shadowMapSampler;
 
 float shadowTest(vec3 worldPos){
     vec4 lightPos = lightMatrix * vec4(worldPos, 1);
@@ -34,13 +35,11 @@ float shadowTest(vec3 worldPos){
 }
 
 void main()	{
-    /*
     vec3 N = normalize(passNormal);
-    vec3 sunColor = vec3(1);
+    vec3 sunColor = vec3(10);
     vec3 sun = sunColor * clamp(dot(N, L), 0, 1);
     sun *= shadowTest(passPos);
-    vec3 ambient = vec3(0.1);
-    vec3 albedo = texture(sampler2D(meshTexture, textureSampler), passUV).rgb;
-    */
-	outColor = passNormal;
+    vec3 ambient = vec3(0.05);
+    vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb;
+	outColor = albedo * (sun + ambient);
 }
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shader.vert b/projects/bloom/resources/shaders/shader.vert
index 0ab82c203806356d0f35dc52c0a6988b286d90d1..926f86af2860cb57c44d2d5ee78712b6ae155e5c 100644
--- a/projects/bloom/resources/shaders/shader.vert
+++ b/projects/bloom/resources/shaders/shader.vert
@@ -16,7 +16,7 @@ layout( push_constant ) uniform constants{
 
 void main()	{
 	gl_Position = mvp * vec4(inPosition, 1.0);
-	passNormal  = inNormal;
+	passNormal  = mat3(model) * inNormal;    // assuming no weird stuff like shearing or non-uniform scaling
     passUV      = inUV;
     passPos     = (model * vec4(inPosition, 1)).xyz;
 }
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shadow_frag.spv b/projects/bloom/resources/shaders/shadow_frag.spv
deleted file mode 100644
index 6be3bd2518a3b1f234e39aea2503ba86cfb3314b..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/shadow_frag.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/shadow_vert.spv b/projects/bloom/resources/shaders/shadow_vert.spv
deleted file mode 100644
index afaa0824ee9be2c22209d611943c6512587dce24..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/shadow_vert.spv and /dev/null differ
diff --git a/projects/bloom/resources/shaders/vert.spv b/projects/bloom/resources/shaders/vert.spv
deleted file mode 100644
index 5e514eef5983927316465679af5461f507497130..0000000000000000000000000000000000000000
Binary files a/projects/bloom/resources/shaders/vert.spv and /dev/null differ
diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp
index e33f603bb92fc58a5ebff6fb042a8ab6a9a7d59f..3b738a4c18295a56e7b0d1d2bc99e26d23c5aff5 100644
--- a/projects/bloom/src/main.cpp
+++ b/projects/bloom/src/main.cpp
@@ -4,13 +4,16 @@
 #include <vkcv/camera/CameraManager.hpp>
 #include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+#include <vkcv/Logger.hpp>
+#include <glm/glm.hpp>
 
 int main(int argc, const char** argv) {
-	const char* applicationName = "First Mesh";
-
-	uint32_t windowWidth = 800;
-	uint32_t windowHeight = 600;
+	const char* applicationName = "Bloom";
 
+	uint32_t windowWidth = 1920;
+	uint32_t windowHeight = 1080;
+	
 	vkcv::Window window = vkcv::Window::create(
 		applicationName,
 		windowWidth,
@@ -18,9 +21,15 @@ int main(int argc, const char** argv) {
 		true
 	);
 
-	vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
-	cameraManager.getCamera().setPosition(glm::vec3(0.f, 0.f, 3.f));
-	cameraManager.getCamera().setNearFar(0.1, 30);
+    vkcv::camera::CameraManager cameraManager(window);
+    uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+    uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
+    
+    cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f));
+    cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f);
+	cameraManager.getCamera(camIndex).setYaw(180.0f);
+	
+	cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f);
 
 	window.initEvents();
 
@@ -33,300 +42,303 @@ int main(int argc, const char** argv) {
 		{ "VK_KHR_swapchain" }
 	);
 
+	const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf";
+	vkcv::asset::Scene scene;
+	int result = vkcv::asset::loadScene(path, scene);
 
-    // ATTACHMENTS
-    const vkcv::AttachmentDescription offscreenAttachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            vk::Format::eB10G11R11UfloatPack32
-    );
-
-    const vkcv::AttachmentDescription shadowAttachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            vk::Format::eD16Unorm
-    );
+	if (result == 1) {
+		std::cout << "Scene loading successful!" << std::endl;
+	}
+	else {
+		std::cout << "Scene loading failed: " << result << std::endl;
+		return 1;
+	}
 
-    const vkcv::AttachmentDescription depthAttachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            vk::Format::eD32Sfloat
-    );
+	// build index and vertex buffers
+	assert(!scene.vertexGroups.empty());
+	std::vector<std::vector<uint8_t>> vBuffers;
+	std::vector<std::vector<uint8_t>> iBuffers;
 
-    const vkcv::AttachmentDescription presentAttachment(
-            vkcv::AttachmentOperation::STORE,
-            vkcv::AttachmentOperation::CLEAR,
-            core.getSwapchainImageFormat()
-    );
+	std::vector<vkcv::VertexBufferBinding> vBufferBindings;
+	std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings;
+	std::vector<vkcv::asset::VertexAttribute> vAttributes;
 
-    // RENDER PASSES
-    const vkcv::PassConfig shadowPassConfig({shadowAttachment});
-    const vkcv::PassHandle shadowPassHandle = core.createPass(shadowPassConfig);
+	for (int i = 0; i < scene.vertexGroups.size(); i++) {
 
-    const vkcv::PassConfig meshPassDefinition({ offscreenAttachment, depthAttachment });
-    const vkcv::PassHandle meshPassHandle = core.createPass(meshPassDefinition);
+		vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data);
+		iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data);
 
-    const vkcv::PassConfig screenQuadPassConfig({presentAttachment});
-    const vkcv::PassHandle screenQuadPassHandle = core.createPass(screenQuadPassConfig);
+		auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes;
 
-    if (!shadowPassHandle || !meshPassHandle || !screenQuadPassHandle)
-    {
-        std::cout << "Error. Could not create render passes. Exiting." << std::endl;
-        return EXIT_FAILURE;
-    }
+		std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
+			return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
+		});
+	}
 
+	std::vector<vkcv::Buffer<uint8_t>> vertexBuffers;
+	for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) {
+		vertexBuffers.push_back(core.createBuffer<uint8_t>(
+			vkcv::BufferType::VERTEX,
+			group.vertexBuffer.data.size()));
+		vertexBuffers.back().fill(group.vertexBuffer.data);
+	}
 
-    // SHADERS
-    vkcv::ShaderProgram shadowProgram;
-    shadowProgram.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv");
-    shadowProgram.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv");
+	std::vector<vkcv::Buffer<uint8_t>> indexBuffers;
+	for (const auto& dataBuffer : iBuffers) {
+		indexBuffers.push_back(core.createBuffer<uint8_t>(
+			vkcv::BufferType::INDEX,
+			dataBuffer.size()));
+		indexBuffers.back().fill(dataBuffer);
+	}
 
-    vkcv::ShaderProgram meshProgram{};
-    meshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv"));
-    meshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv"));
+	int vertexBufferIndex = 0;
+	for (const auto& vertexGroup : scene.vertexGroups) {
+		for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
+			vAttributes.push_back(attribute);
+			vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle()));
+		}
+		vertexBufferBindings.push_back(vBufferBindings);
+		vBufferBindings.clear();
+		vertexBufferIndex++;
+	}
 
-    vkcv::ShaderProgram bloomComputeProgram{};
-    bloomComputeProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/blur_comp.spv"));
+	const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32;
+	const vkcv::AttachmentDescription color_attachment(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		colorBufferFormat
+	);
+	
+	const vk::Format depthBufferFormat = vk::Format::eD32Sfloat;
+	const vkcv::AttachmentDescription depth_attachment(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		depthBufferFormat
+	);
 
-    vkcv::ShaderProgram screenQuadProgram{};
-    screenQuadProgram.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/quad_vert.spv");
-    screenQuadProgram.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/quad_frag.spv");
+	vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment });
+	vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition);
 
+	vkcv::shader::GLSLCompiler compiler;
 
-    vkcv::asset::Mesh mesh;
+	vkcv::ShaderProgram forwardProgram;
+	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), 
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		forwardProgram.addShader(shaderStage, path);
+	});
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		forwardProgram.addShader(shaderStage, path);
+	});
 
-	const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
-	int result = vkcv::asset::loadMesh(path, mesh);
+	const std::vector<vkcv::VertexAttachment> vertexAttachments = forwardProgram.getVertexAttachments();
 
-	if (result == 1) {
-		std::cout << "Mesh loading successful!" << std::endl;
-	}
-	else {
-		std::cout << "Mesh loading failed: " << result << std::endl;
-		return 1;
+	std::vector<vkcv::VertexBinding> vertexBindings;
+	for (size_t i = 0; i < vertexAttachments.size(); i++) {
+		vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
 	}
-	assert(mesh.vertexGroups.size() > 0);
+	const vkcv::VertexLayout vertexLayout (vertexBindings);
+
+	// shadow map
+	vkcv::SamplerHandle shadowSampler = core.createSampler(
+		vkcv::SamplerFilterType::NEAREST,
+		vkcv::SamplerFilterType::NEAREST,
+		vkcv::SamplerMipmapMode::NEAREST,
+		vkcv::SamplerAddressMode::CLAMP_TO_EDGE
+	);
+	const vk::Format shadowMapFormat = vk::Format::eD16Unorm;
+	const uint32_t shadowMapResolution = 1024;
+	const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1);
+
+	// light info buffer
+	struct LightInfo {
+		glm::vec3 direction;
+		float padding;
+		glm::mat4 lightMatrix;
+	};
+	LightInfo lightInfo;
+	vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3));
+
+	vkcv::DescriptorSetHandle forwardShadingDescriptorSet = 
+		core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] });
+
+	vkcv::DescriptorWrites forwardDescriptorWrites;
+	forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) };
+	forwardDescriptorWrites.sampledImageWrites  = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) };
+	forwardDescriptorWrites.samplerWrites       = { vkcv::SamplerDescriptorWrite(2, shadowSampler) };
+	core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites);
+
+	vkcv::SamplerHandle colorSampler = core.createSampler(
+		vkcv::SamplerFilterType::LINEAR,
+		vkcv::SamplerFilterType::LINEAR,
+		vkcv::SamplerMipmapMode::LINEAR,
+		vkcv::SamplerAddressMode::REPEAT
+	);
 
-    auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
+	// prepare per mesh descriptor sets
+	std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets;
+	std::vector<vkcv::Image> sceneImages;
+	for (const auto& vertexGroup : scene.vertexGroups) {
+		perMeshDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1]));
 
-    std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-        return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-    });
+		const auto& material = scene.materials[vertexGroup.materialIndex];
 
-    // MESH INDEX/VERTEX BUFFER
-	auto meshVertexBuffer = core.createBuffer<uint8_t>(
-			vkcv::BufferType::VERTEX,
-			mesh.vertexGroups[0].vertexBuffer.data.size(),
-			vkcv::BufferMemoryType::DEVICE_LOCAL
-	);
-    meshVertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
+		int baseColorIndex = material.baseColor;
+		if (baseColorIndex < 0) {
+			vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color");
+			baseColorIndex = 0;
+		}
 
-	auto meshIndexBuffer = core.createBuffer<uint8_t>(
-			vkcv::BufferType::INDEX,
-			mesh.vertexGroups[0].indexBuffer.data.size(),
-			vkcv::BufferMemoryType::DEVICE_LOCAL
-	);
-    meshIndexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
-
-    // MESH VERTEX LAYOUT
-    const std::vector<vkcv::VertexBufferBinding> meshVertexBufferBindings = {
-            vkcv::VertexBufferBinding(attributes[0].offset, meshVertexBuffer.getVulkanHandle()),
-            vkcv::VertexBufferBinding(attributes[1].offset, meshVertexBuffer.getVulkanHandle()),
-            vkcv::VertexBufferBinding(attributes[2].offset, meshVertexBuffer.getVulkanHandle()) };
-
-    const std::vector<vkcv::VertexAttachment> meshVertexAttachments = meshProgram.getVertexAttachments();
-    std::vector<vkcv::VertexBinding> meshBindings;
-    for (size_t i = 0; i < meshVertexAttachments.size(); i++) {
-        meshBindings.push_back(vkcv::VertexBinding(i, { meshVertexAttachments[i] }));
-    }
-    const vkcv::VertexLayout meshVertexLayout(meshBindings);
-
-    const vkcv::Mesh loadedMesh(meshVertexBufferBindings, meshIndexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices);
-
-    // QUAD INDEX/VERTEX BUFFER
-	const std::vector<float> quadFloats = {-1.0f,  1.0f, 0.0f,  // Left Bottom
-                                            1.0f,  1.0f, 0.0f,  // Right Bottom
-                                           -1.0f, -1.0f, 0.0f,  // Left Top
-                                            1.0f, -1.0f, 0.0f}; // Right Top
-
-    const std::vector<uint8_t> quadIndices = {0, 1, 2,
-                                              2, 1, 3};
-
-    auto quadVertexBuffer = core.createBuffer<float>(
-            vkcv::BufferType::VERTEX,
-            quadFloats.size(),
-            vkcv::BufferMemoryType::DEVICE_LOCAL);
-    quadVertexBuffer.fill(quadFloats);
-
-    auto quadIndexBuffer = core.createBuffer<uint8_t>(
-            vkcv::BufferType::INDEX,
-            quadIndices.size(),
-            vkcv::BufferMemoryType::DEVICE_LOCAL);
-    quadIndexBuffer.fill(quadIndices);
-
-	// SCREEN QUAD VERTEX LAYOUT
-	const std::vector<vkcv::VertexBufferBinding> quadVertexBufferBindings = {
-	        vkcv::VertexBufferBinding(0, quadVertexBuffer.getVulkanHandle())
-	};
-	const vkcv::VertexBinding quadBinding(0, screenQuadProgram.getVertexAttachments());
-	const vkcv::VertexLayout quadLayout({quadBinding});
-
-    const vkcv::Mesh loadedQuad(quadVertexBufferBindings, quadIndexBuffer.getVulkanHandle(), quadIndices.size());
-
-	// RESOURCES
-    vkcv::Image meshTexture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h);
-    meshTexture.fill(mesh.texture_hack.img);
-    const vkcv::ImageHandle meshTextureHandle = meshTexture.getHandle();
-
-    vkcv::ImageHandle depthBufferHandle = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle();
-
-    vkcv::Image offscreenImage = core.createImage(vk::Format::eB10G11R11UfloatPack32, windowWidth, windowHeight, 1);
-    vkcv::ImageHandle bloomImageHandle = core.createImage(vk::Format::eB10G11R11UfloatPack32, windowWidth, windowHeight, 1).getHandle();
-
-    const vkcv::ImageHandle swapchainHandle = vkcv::ImageHandle::createSwapchainImageHandle();
-
-
-    const vkcv::ImageHandle shadowMapHandle = core.createImage(vk::Format::eD16Unorm, 1024, 1024, 1).getHandle();
-
-    const vkcv::SamplerHandle linearSampler = core.createSampler(
-            vkcv::SamplerFilterType::LINEAR,
-            vkcv::SamplerFilterType::LINEAR,
-            vkcv::SamplerMipmapMode::LINEAR,
-            vkcv::SamplerAddressMode::REPEAT
-    );
-
-    const vkcv::SamplerHandle shadowSampler = core.createSampler(
-            vkcv::SamplerFilterType::NEAREST,
-            vkcv::SamplerFilterType::NEAREST,
-            vkcv::SamplerMipmapMode::NEAREST,
-            vkcv::SamplerAddressMode::CLAMP_TO_EDGE
-    );
-
-
-    struct LightInfo {
-        glm::vec3 direction;
-        float padding;
-        glm::mat4 lightMatrix;
-    };
-    LightInfo lightInfo{};
-    vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3));
-
-    // PIPELINES & DESCRIPTOR STUFF
-	const std::vector<vkcv::DescriptorBinding> meshDescriptorBindings = { meshProgram.getReflectedDescriptors()[0] };
-	const vkcv::DescriptorSetHandle meshDescriptorSet = core.createDescriptorSet(meshDescriptorBindings);
-    const vkcv::DescriptorSetUsage meshDescriptorUsage(0, core.getDescriptorSet(meshDescriptorSet).vulkanHandle);
-
-    vkcv::DescriptorWrites meshDescriptorWrites;
-    meshDescriptorWrites.sampledImageWrites    = {
-            vkcv::SampledImageDescriptorWrite(0, meshTextureHandle),
-            vkcv::SampledImageDescriptorWrite(3, shadowMapHandle) };
-    meshDescriptorWrites.samplerWrites         = {
-            vkcv::SamplerDescriptorWrite(1, linearSampler),
-            vkcv::SamplerDescriptorWrite(4, shadowSampler) };
-    meshDescriptorWrites.uniformBufferWrites   = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) };
-    core.writeDescriptorSet(meshDescriptorSet, meshDescriptorWrites);
-
-    const vkcv::PipelineConfig meshPipelineConfig(
-        meshProgram,
+		vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex];
+
+		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h));
+		sceneImages.back().fill(sceneTexture.data.data());
+
+		vkcv::DescriptorWrites setWrites;
+		setWrites.sampledImageWrites = {
+			vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle())
+		};
+		setWrites.samplerWrites = {
+			vkcv::SamplerDescriptorWrite(1, colorSampler),
+		};
+		core.writeDescriptorSet(perMeshDescriptorSets.back(), setWrites);
+	}
+
+	const vkcv::PipelineConfig forwardPipelineConfig {
+		forwardProgram,
 		windowWidth,
 		windowHeight,
-        meshPassHandle,
-        {meshVertexLayout},
-		{ core.getDescriptorSet(meshDescriptorSet).layout },
-		true);
-	vkcv::PipelineHandle meshPipelineHandle = core.createGraphicsPipeline(meshPipelineConfig);
-
-	// --
-
-    const vkcv::PipelineConfig shadowPipeConfig(
-            shadowProgram,
-            1024,
-            1024,
-            shadowPassHandle,
-            {meshVertexLayout},
-            {},
-            false);
-    const vkcv::PipelineHandle shadowPipelineHandle = core.createGraphicsPipeline(shadowPipeConfig);
-
-    // --
-
-    const std::vector<vkcv::DescriptorBinding> bloomSetBindings = bloomComputeProgram.getReflectedDescriptors()[0];
-    const vkcv::DescriptorSetHandle bloomSetHandle = core.createDescriptorSet(bloomSetBindings);
-    const vkcv::DescriptorSetUsage bloomSetUsage(0, core.getDescriptorSet(bloomSetHandle).vulkanHandle);
-
-    vkcv::DescriptorWrites bloomSetWrites;
-    bloomSetWrites.sampledImageWrites  = {vkcv::SampledImageDescriptorWrite(0, offscreenImage.getHandle())};
-    bloomSetWrites.samplerWrites       = {vkcv::SamplerDescriptorWrite(1, linearSampler)};
-    bloomSetWrites.storageImageWrites  = {vkcv::StorageImageDescriptorWrite(2, bloomImageHandle)};
-    core.writeDescriptorSet(bloomSetHandle, bloomSetWrites);
-    vkcv::PipelineHandle bloomComputePipelineHandle = core.createComputePipeline(bloomComputeProgram, {core.getDescriptorSet(bloomSetHandle).layout});
-
-    // --
-    const std::vector<vkcv::DescriptorBinding> screenQuadBindings = { screenQuadProgram.getReflectedDescriptors()[0] };
-    const vkcv::DescriptorSetHandle screenQuadSet = core.createDescriptorSet(screenQuadBindings);
-    const vkcv::DescriptorSetUsage screenQuadDescriptorUsage(0, core.getDescriptorSet(screenQuadSet).vulkanHandle);
-
-    vkcv::DescriptorWrites screenQuadSetDescriptorWrites;
-    screenQuadSetDescriptorWrites.sampledImageWrites    = {
-            vkcv::SampledImageDescriptorWrite(0, offscreenImage.getHandle()),
-            vkcv::SampledImageDescriptorWrite(1, bloomImageHandle) };
-    screenQuadSetDescriptorWrites.samplerWrites         = {
-            vkcv::SamplerDescriptorWrite(2, linearSampler) };
-    core.writeDescriptorSet(screenQuadSet, screenQuadSetDescriptorWrites);
-
-    const vkcv::PipelineConfig screenQuadConfig(
-            screenQuadProgram,
-            windowWidth,
-            windowHeight,
-            screenQuadPassHandle,
-            {quadLayout},
-            { core.getDescriptorSet(meshDescriptorSet).layout },
-            true);
-    vkcv::PipelineHandle screenQuadPipelineHandle = core.createGraphicsPipeline(screenQuadConfig);
-
-    if (!meshPipelineHandle || !shadowPipelineHandle || !bloomComputePipelineHandle) {
-        std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
-        return EXIT_FAILURE;
-    }
-
-    // DRAWCALLS AND MVP STUFF
-
-	const std::vector<glm::vec3> cubeTranslations = {
-		glm::vec3( 0.f, -2.f, 0.f),
-		glm::vec3( 3.f,  0.f, 0.f),
-		glm::vec3(-3.f,  0.f, 0.f),
-		glm::vec3( 0.f,  2.f, 0.f),
-		glm::vec3( 0.f, -5.f, 0.f)
+		forwardPass,
+		vertexLayout,
+		{	core.getDescriptorSet(forwardShadingDescriptorSet).layout, 
+			core.getDescriptorSet(perMeshDescriptorSets[0]).layout },
+		true
 	};
-
-	std::vector<glm::mat4> modelMatrices;
-	std::vector<vkcv::DrawcallInfo> meshDrawcalls;
-	std::vector<vkcv::DrawcallInfo> shadowDrawcalls;
-	for (const auto& translationVec : cubeTranslations) {
-		modelMatrices.push_back(glm::translate(glm::mat4(1.f), translationVec));
-        meshDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { meshDescriptorUsage }));
-		shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {}));
+	
+	vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig);
+	
+	if (!forwardPipeline) {
+		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
+		return EXIT_FAILURE;
 	}
 
-	vkcv::DrawcallInfo quadDrawcall(loadedQuad, {screenQuadDescriptorUsage});
-
-	modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f));
+	vkcv::ImageHandle depthBuffer       = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle();
+	vkcv::ImageHandle colorBuffer       = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, true).getHandle();
+	vkcv::ImageHandle blurBuffer        = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, false).getHandle();
+	vkcv::SamplerHandle linearSampler   = core.createSampler(vkcv::SamplerFilterType::LINEAR,
+                                                             vkcv::SamplerFilterType::LINEAR,
+                                                             vkcv::SamplerMipmapMode::LINEAR,
+                                                             vkcv::SamplerAddressMode::CLAMP_TO_EDGE);
+
+	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+
+	vkcv::ShaderProgram shadowShader;
+	compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert",
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		shadowShader.addShader(shaderStage, path);
+	});
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag",
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		shadowShader.addShader(shaderStage, path);
+	});
+
+	const std::vector<vkcv::AttachmentDescription> shadowAttachments = {
+		vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat)
+	};
+	const vkcv::PassConfig shadowPassConfig(shadowAttachments);
+	const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig);
+	const vkcv::PipelineConfig shadowPipeConfig{
+		shadowShader,
+		shadowMapResolution,
+		shadowMapResolution,
+		shadowPass,
+		vertexLayout,
+		{},
+		false
+	};
+	const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig);
 
 	std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
 	std::vector<glm::mat4> mvpLight;
 
+	// gamma correction compute shader
+	vkcv::ShaderProgram gammaCorrectionProgram;
+	compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp",
+		[&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		gammaCorrectionProgram.addShader(shaderStage, path);
+	});
+	vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]);
+	vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram,
+		{ core.getDescriptorSet(gammaCorrectionDescriptorSet).layout });
+
+	// blur compute shader
+	vkcv::ShaderProgram blurProgram;
+	compiler.compile(vkcv::ShaderStage::COMPUTE,
+                     "resources/shaders/blur.comp",
+                     [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path)
+                     {
+                         blurProgram.addShader(shaderStage, path);
+                     });
+	vkcv::DescriptorSetHandle blurDescriptorSet = core.createDescriptorSet(blurProgram.getReflectedDescriptors()[0]);
+	vkcv::PipelineHandle blurPipeline = core.createComputePipeline(blurProgram,
+                                                                   { core.getDescriptorSet(blurDescriptorSet).layout });
+
+    // bloom composite shader
+    vkcv::ShaderProgram compositeBloomProgram;
+    compiler.compile(vkcv::ShaderStage::COMPUTE,
+                     "resources/shaders/compositeBloom.comp",
+                     [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path)
+                     {
+                         compositeBloomProgram.addShader(shaderStage, path);
+                     });
+    vkcv::DescriptorSetHandle compositeBloomDescriptorSet = core.createDescriptorSet(compositeBloomProgram.getReflectedDescriptors()[0]);
+    vkcv::PipelineHandle compositeBloomPipeline = core.createComputePipeline(compositeBloomProgram,
+                                                                   { core.getDescriptorSet(compositeBloomDescriptorSet).layout });
+
+	// model matrices per mesh
+	std::vector<glm::mat4> modelMatrices;
+	modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f));
+	for (const auto& mesh : scene.meshes) {
+		const glm::mat4 m = *reinterpret_cast<const glm::mat4*>(&mesh.modelMatrix[0]);
+		for (const auto& vertexGroupIndex : mesh.vertexGroups) {
+			modelMatrices[vertexGroupIndex] = m;
+		}
+	}
+
+	// prepare drawcalls
+	std::vector<vkcv::Mesh> meshes;
+	for (int i = 0; i < scene.vertexGroups.size(); i++) {
+		vkcv::Mesh mesh(
+			vertexBufferBindings[i], 
+			indexBuffers[i].getVulkanHandle(), 
+			scene.vertexGroups[i].numIndices);
+		meshes.push_back(mesh);
+	}
+
+	std::vector<vkcv::DrawcallInfo> drawcalls;
+	std::vector<vkcv::DrawcallInfo> shadowDrawcalls;
+	for (int i = 0; i < meshes.size(); i++) {
+
+		drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { 
+			vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle),
+			vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) }));
+		shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {}));
+	}
 
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
-    while (window.isWindowOpen()) {
+	while (window.isWindowOpen()) {
 		vkcv::Window::pollEvents();
 
 		uint32_t swapchainWidth, swapchainHeight;
 		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
 			continue;
 		}
+
 		if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) {
-            depthBufferHandle = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle();
+			colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, true, true).getHandle();
+            blurBuffer  = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, false).getHandle();
 
 			windowWidth = swapchainWidth;
 			windowHeight = swapchainHeight;
@@ -334,13 +346,38 @@ int main(int argc, const char** argv) {
 
 		auto end = std::chrono::system_clock::now();
 		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+
+		// update descriptor sets which use swapchain image
+        // blur
+        vkcv::DescriptorWrites blurDescriptorWrites;
+        blurDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorBuffer)};
+        blurDescriptorWrites.samplerWrites      = {vkcv::SamplerDescriptorWrite(1, linearSampler)};
+        blurDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, blurBuffer) };
+        core.writeDescriptorSet(blurDescriptorSet, blurDescriptorWrites);
+
+        // composite bloom
+        vkcv::DescriptorWrites compositeBloomDescriptorWrites;
+        compositeBloomDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, blurBuffer)};
+        compositeBloomDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)};
+        compositeBloomDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, colorBuffer)};
+        core.writeDescriptorSet(compositeBloomDescriptorSet, compositeBloomDescriptorWrites);
+
+        // gamma correction
+        vkcv::DescriptorWrites gammaCorrectionDescriptorWrites;
+        gammaCorrectionDescriptorWrites.storageImageWrites = {
+                vkcv::StorageImageDescriptorWrite(0, colorBuffer),
+                vkcv::StorageImageDescriptorWrite(1, swapchainInput) };
+        core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites);
+
 		start = end;
-		cameraManager.getCamera().updateView(deltatime.count() * 0.000001);
+		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
 
-		const float sunTheta = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime).count() * 0.001f;
+		auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime);
+		
+		const float sunTheta = 0.0001f * static_cast<float>(duration.count());
 		lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta)));
 
-		const float shadowProjectionSize = 5.f;
+		const float shadowProjectionSize = 20.f;
 		glm::mat4 projectionLight = glm::ortho(
 			-shadowProjectionSize,
 			shadowProjectionSize,
@@ -359,70 +396,85 @@ int main(int argc, const char** argv) {
 		lightInfo.lightMatrix = projectionLight * viewLight;
 		lightBuffer.fill({ lightInfo });
 
-		const glm::mat4 viewProjectionCamera = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
+		const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP();
 
 		mainPassMatrices.clear();
 		mvpLight.clear();
 		for (const auto& m : modelMatrices) {
 			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
-			mvpLight.push_back(lightInfo.lightMatrix* m);
+			mvpLight.push_back(lightInfo.lightMatrix * m);
 		}
 
 		vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
+		const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer };
 
-		vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+		const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		// shadow map pass
+		// shadow map
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			shadowPassHandle,
-			shadowPipelineHandle,
+			shadowPass,
+			shadowPipe,
 			shadowPushConstantData,
 			shadowDrawcalls,
-			{ shadowMapHandle });
+			{ shadowMap.getHandle() });
+		core.prepareImageForSampling(cmdStream, shadowMap.getHandle());
 
-		core.prepareImageForSampling(cmdStream, shadowMapHandle);
-
-        // offscreen render pass
+		// main pass
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-            meshPassHandle,
-            meshPipelineHandle,
+            forwardPass,
+            forwardPipeline,
 			pushConstantData,
-			meshDrawcalls,
-            {offscreenImage.getHandle(), depthBufferHandle});
+			drawcalls,
+			renderTargets);
 
+		const uint32_t gammaCorrectionLocalGroupSize = 8;
+		const uint32_t gammaCorrectionDispatchCount[3] = {
+			static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(gammaCorrectionLocalGroupSize))),
+			static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(gammaCorrectionLocalGroupSize))),
+			1
+		};
 
-		core.prepareImageForSampling(cmdStream, offscreenImage.getHandle());
-		//core.prepareImageForStorage(cmdStream, bloomAttachment.getHandle());
+		core.prepareImageForSampling(cmdStream, colorBuffer);
+		core.prepareImageForStorage(cmdStream, blurBuffer);
 
-		// compute blur pass
-		const std::array<uint32_t, 3> dispatchCount = {windowWidth, windowHeight, 0};
-		/*
+		// blur dispatch
 		core.recordComputeDispatchToCmdStream(
 		        cmdStream,
-		        bloomComputePipeline,
-		        dispatchCount.data(),
-                {bloomSetUsage},
+		        blurPipeline,
+		        gammaCorrectionDispatchCount,
+                {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(blurDescriptorSet).vulkanHandle)},
                 vkcv::PushConstantData(nullptr, 0));
-        */
-		// final compositing screen quad pass
-		core.recordDrawcallsToCmdStream(
-		        cmdStream,
-		        screenQuadPassHandle,
-		        screenQuadPipelineHandle,
-		        vkcv::PushConstantData(nullptr, 0),
-                {quadDrawcall},
-                {swapchainHandle}
-		        );
 
+		core.prepareImageForStorage(cmdStream, colorBuffer);
+		core.prepareImageForSampling(cmdStream, blurBuffer);
+
+		// bloom composite dispatch
+        core.recordComputeDispatchToCmdStream(cmdStream,
+                                              compositeBloomPipeline,
+                                              gammaCorrectionDispatchCount,
+                                              {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(compositeBloomDescriptorSet).vulkanHandle)},
+                                              vkcv::PushConstantData(nullptr, 0));
+
+        core.prepareImageForStorage(cmdStream, swapchainInput);
+
+		// gamma correction dispatch
+		core.recordComputeDispatchToCmdStream(
+			cmdStream, 
+			gammaCorrectionPipeline, 
+			gammaCorrectionDispatchCount,
+			{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) },
+			vkcv::PushConstantData(nullptr, 0));
+
+		// present and end
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 
 		core.endFrame();
 	}
-
+	
 	return 0;
 }