From 366b2060ba85c3d753338d97497b50b6ce3faed0 Mon Sep 17 00:00:00 2001
From: Artur Wasmut <awasmut@uni-koblenz.de>
Date: Tue, 15 Jun 2021 19:02:34 +0200
Subject: [PATCH] some blur/bloom project.

---
 include/vkcv/Core.hpp                         |   1 +
 include/vkcv/DescriptorWrites.hpp             |   6 +-
 projects/CMakeLists.txt                       |   1 +
 projects/bloom/.gitignore                     |   1 +
 projects/bloom/CMakeLists.txt                 |  28 ++
 .../bloom/resources/cube/boards2_vcyc_jpg.jpg |   3 +
 projects/bloom/resources/cube/cube.bin        |   3 +
 projects/bloom/resources/cube/cube.blend      |   3 +
 projects/bloom/resources/cube/cube.blend1     |   3 +
 projects/bloom/resources/cube/cube.glb        |   3 +
 projects/bloom/resources/cube/cube.gltf       |   3 +
 projects/bloom/resources/shaders/blur.comp    |  17 +
 .../bloom/resources/shaders/blur_comp.spv     | Bin 0 -> 1316 bytes
 projects/bloom/resources/shaders/compile.bat  |  11 +
 projects/bloom/resources/shaders/frag.spv     | Bin 0 -> 1300 bytes
 projects/bloom/resources/shaders/quad.frag    |  15 +
 projects/bloom/resources/shaders/quad.vert    |  12 +
 .../bloom/resources/shaders/quad_frag.spv     | Bin 0 -> 920 bytes
 .../bloom/resources/shaders/quad_vert.spv     | Bin 0 -> 1076 bytes
 projects/bloom/resources/shaders/shader.frag  |  46 ++
 projects/bloom/resources/shaders/shader.vert  |  22 +
 projects/bloom/resources/shaders/shadow.frag  |   6 +
 projects/bloom/resources/shaders/shadow.vert  |  12 +
 .../bloom/resources/shaders/shadow_frag.spv   | Bin 0 -> 288 bytes
 .../bloom/resources/shaders/shadow_vert.spv   | Bin 0 -> 1184 bytes
 projects/bloom/resources/shaders/vert.spv     | Bin 0 -> 1872 bytes
 projects/bloom/src/main.cpp                   | 428 ++++++++++++++++++
 src/vkcv/Core.cpp                             |   6 +
 28 files changed, 627 insertions(+), 3 deletions(-)
 create mode 100644 projects/bloom/.gitignore
 create mode 100644 projects/bloom/CMakeLists.txt
 create mode 100644 projects/bloom/resources/cube/boards2_vcyc_jpg.jpg
 create mode 100644 projects/bloom/resources/cube/cube.bin
 create mode 100644 projects/bloom/resources/cube/cube.blend
 create mode 100644 projects/bloom/resources/cube/cube.blend1
 create mode 100644 projects/bloom/resources/cube/cube.glb
 create mode 100644 projects/bloom/resources/cube/cube.gltf
 create mode 100644 projects/bloom/resources/shaders/blur.comp
 create mode 100644 projects/bloom/resources/shaders/blur_comp.spv
 create mode 100644 projects/bloom/resources/shaders/compile.bat
 create mode 100644 projects/bloom/resources/shaders/frag.spv
 create mode 100644 projects/bloom/resources/shaders/quad.frag
 create mode 100644 projects/bloom/resources/shaders/quad.vert
 create mode 100644 projects/bloom/resources/shaders/quad_frag.spv
 create mode 100644 projects/bloom/resources/shaders/quad_vert.spv
 create mode 100644 projects/bloom/resources/shaders/shader.frag
 create mode 100644 projects/bloom/resources/shaders/shader.vert
 create mode 100644 projects/bloom/resources/shaders/shadow.frag
 create mode 100644 projects/bloom/resources/shaders/shadow.vert
 create mode 100644 projects/bloom/resources/shaders/shadow_frag.spv
 create mode 100644 projects/bloom/resources/shaders/shadow_vert.spv
 create mode 100644 projects/bloom/resources/shaders/vert.spv
 create mode 100644 projects/bloom/src/main.cpp

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 4a51b24f..48e94aad 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -279,5 +279,6 @@ namespace vkcv
 		void submitCommandStream(const CommandStreamHandle handle);
 		void prepareSwapchainImageForPresent(const CommandStreamHandle handle);
 		void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image);
+        void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image);
     };
 }
diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp
index d67e8e32..c9d04656 100644
--- a/include/vkcv/DescriptorWrites.hpp
+++ b/include/vkcv/DescriptorWrites.hpp
@@ -36,8 +36,8 @@ namespace vkcv {
 	struct DescriptorWrites {
 		std::vector<SampledImageDescriptorWrite>		sampledImageWrites;
 		std::vector<StorageImageDescriptorWrite>		storageImageWrites;
-		std::vector<UniformBufferDescriptorWrite>	uniformBufferWrites;
-		std::vector<StorageBufferDescriptorWrite>	storageBufferWrites;
-		std::vector<SamplerDescriptorWrite>			samplerWrites;
+		std::vector<UniformBufferDescriptorWrite>	    uniformBufferWrites;
+		std::vector<StorageBufferDescriptorWrite>	    storageBufferWrites;
+		std::vector<SamplerDescriptorWrite>			    samplerWrites;
 	};
 }
\ No newline at end of file
diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt
index ccbdaf41..472597e0 100644
--- a/projects/CMakeLists.txt
+++ b/projects/CMakeLists.txt
@@ -1,5 +1,6 @@
 
 # Add new projects/examples here:
+add_subdirectory(bloom)
 add_subdirectory(first_triangle)
 add_subdirectory(first_mesh)
 add_subdirectory(cmd_sync_test)
\ No newline at end of file
diff --git a/projects/bloom/.gitignore b/projects/bloom/.gitignore
new file mode 100644
index 00000000..3643183e
--- /dev/null
+++ b/projects/bloom/.gitignore
@@ -0,0 +1 @@
+bloom
\ No newline at end of file
diff --git a/projects/bloom/CMakeLists.txt b/projects/bloom/CMakeLists.txt
new file mode 100644
index 00000000..bcd8827f
--- /dev/null
+++ b/projects/bloom/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.16)
+project(bloom)
+
+# setting c++ standard for the project
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# this should fix the execution path to load local files from the project
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+# adding source files to the project
+add_executable(bloom src/main.cpp)
+
+# this should fix the execution path to load local files from the project (for MSVC)
+if(MSVC)
+	set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+	set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+    
+    # in addition to setting the output directory, the working directory has to be set
+	# by default visual studio sets the working directory to the build directory, when using the debugger
+	set_target_properties(bloom PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+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})
+
+# 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)
diff --git a/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg b/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg
new file mode 100644
index 00000000..2636039e
--- /dev/null
+++ b/projects/bloom/resources/cube/boards2_vcyc_jpg.jpg
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..3303cd86
--- /dev/null
+++ b/projects/bloom/resources/cube/cube.bin
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..62ccb2c7
--- /dev/null
+++ b/projects/bloom/resources/cube/cube.blend
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..13f21dcc
--- /dev/null
+++ b/projects/bloom/resources/cube/cube.blend1
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..66a42c65
--- /dev/null
+++ b/projects/bloom/resources/cube/cube.glb
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..42817614
--- /dev/null
+++ b/projects/bloom/resources/cube/cube.gltf
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 00000000..d7fcfb3c
--- /dev/null
+++ b/projects/bloom/resources/shaders/blur.comp
@@ -0,0 +1,17 @@
+# version 450
+layout(local_size_x = 8, local_size_y = 8) 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);
+
+    // TODO: BLUR
+
+
+    imageStore(blurAttachment, pixel_coords, vec4(1.0, 0.0 ,0.0, 0.0));
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/blur_comp.spv b/projects/bloom/resources/shaders/blur_comp.spv
new file mode 100644
index 0000000000000000000000000000000000000000..565eab96c0b2c554556077dee67d21ed92f42764
GIT binary patch
literal 1316
zcmZQ(Qf6mhU}WH8;APNbfB-=TCI&_Z1_lsq2&Ih}7#O%2*udiMKEXbE#U&|vCZ+};
z9efPTAPOwU&%nmO%D}+D!oa|go0ypglHg@vU=Uzn1B(eVFfedHF~|;P24)5(1_p*h
zj37N+4DLSh?*9JnKCbb}1qJcNC6zg;@j01!sqrb9MXAXpnPsUUbsR|QGV_viN>h*(
zurjcK?Sk3Q%D~3J$-ux+kXezM6Q7))UzAb|au+KD2Ls4$={fQ4Ir&M6Ii7iC`N@eT
znfZC1E({C|tPIQyq6`cSrDY&C8-o-B14B|yX^~?|Nn&zFZfaf$NSvKPi-Cb5KP{~|
zxhOR?4@r`hft^7YSuQv+w;(6A2xPZA0}I%0ZUzPh83qOh5Fcc|5=at>ugJgv4j*?0
zX0ZK|P&svQd@wM;_#ihR>jm*adO^m4%m%pwSsug($%EoRn1Kmwhav+gHX!ziGO#eP
zF)%PFFo04A1A{mN3j;d?16T|cUkVH?;CKXyfiOrt7gRmSPLO(#-#}uZ)B#e@12qdI
zZq2~L0OEtfALIs*dOijQ29SD?`C<&r3_?)zKytzi3}8M;4rG=H0|VGSAaO|sP6i36
zJjg7N{V)tt4@z$!J)p1xiG$RF><95dVGa@l*#Qa<ke#5ktIfaywht0!4fZg;GDtB4
z1A`6&I|E3)3IhWJNDW9FhCymU{!oRca~NL@>VF*uHU>}_fZ`CO282O!Ab)7!@CVFa
zAb)5=-2hSx@&||yif@px6ay23HaOf^7<51a3=CjCNF9g|ig%E(83QXg&Ov?!@j-F)
zgMkU0cR^}FVFt=GptuF`LE#O`3n0EN0}I&w{0t1>H0;m7&H#!#0R{#JkQfMq%mlf)
z2AXa_`5q*1$G{BEiy%J89FTcp3=Ckgd<GT<P<)CrFff3`K=Potl4f84r~M45S)lv^
z5`(Fih326S23D|sIR;RAWnch_gX{yj8zcrZPabL>h!3(Kl*U1RO=e(W0Lg*)pm_bu
Lz{p_1z{mgqBt&0J

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/compile.bat b/projects/bloom/resources/shaders/compile.bat
new file mode 100644
index 00000000..5fecf915
--- /dev/null
+++ b/projects/bloom/resources/shaders/compile.bat
@@ -0,0 +1,11 @@
+%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/frag.spv b/projects/bloom/resources/shaders/frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..ba47f91cdfcd1ecd824614f23d424b0a510ac1c7
GIT binary patch
literal 1300
zcmZQ(Qf6mhU}WH8;AN0wfB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z44e!s3=CkLo0ypglHdgEWnkcEU|<jcV`i{CI|Bm)GXpaN69WUoAx4lMP6l_Mc*h{8
z_~O)p#G=HK)cE3z#FW&c`23`-)Z~(4kQy$K8h3wxcOTdI<bs0u;*!do)cBmtywv!V
z%%arflFYJHkU9<|b(wj|Ii)Gc3RoFfz;1xKk(Gg!0p!m7(h}$Voctn?yFp^y3=9kf
ziN(c!`9-;jISdRSeIS2;#6rU$e2||&{DAyo29O?M1_p-Q)Z&bg)QXbQqErS}1~vvU
z1_p)_2tPP6w;(6A2&9*lL6U)ip|~{9sWdGOB*ehRzycNn+3&-^z`(`;Q3DG9g2a@R
z%)E34HU>7Z8c?|9WTt17_$HPVWmYh-GB7hpgETTg%$5O}ospQ5U+$Y&02X6skVO`Q
zyM%$koq+}He^{J@_#nR{^FjUw@j-3@$%Fh2;)C1);)DFH1hpT=S4852!T?zw#0SZP
z%=3Wy6Qo=jL@+Qgz{EiEATb`W7$kf^cCv!`knjQVL25v5g6RW^fzlXA%$<Q59%dl7
zfx-#dEg(L~Eg(H0c~ICP%Y*nJc~D9R$%DcXSsug($%E3qFar}fO)G*^Hv^b0#=y+L
z#=yX!z`(#D&cMO|au<jXGEaelg@FT{7Z||e)=)kuJ;LO<7(lraArIn%;s+EqAoF;^
zX$4{z$UHu%dXPBCJP;oge<1ZBK1e?(TtIwD22KV+sD2P18vI}i#s`@x1QuZcs|A?>
z;)CKGBrL_i#2^Cp9}9yhNPvL>%m=9h@j-D75(c?d9BLORtRxs%7$m@90?M-t406zP
z01F?G9LR1d28iE4`2}Pqh!2W0P<(;-Ah&?x2xJF{53&;!Cm_BV11mTVK=y(7pfLTx
hz{CJbKeo`g0HrNZc-k>AgYyB1|CfQ0!GeL20RZrSZgl_v

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/quad.frag b/projects/bloom/resources/shaders/quad.frag
new file mode 100644
index 00000000..14bacc95
--- /dev/null
+++ b/projects/bloom/resources/shaders/quad.frag
@@ -0,0 +1,15 @@
+#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
new file mode 100644
index 00000000..a5d52008
--- /dev/null
+++ b/projects/bloom/resources/shaders/quad.vert
@@ -0,0 +1,12 @@
+#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
new file mode 100644
index 0000000000000000000000000000000000000000..86f9763845401abb1f01879b58b234ffb5904efc
GIT binary patch
literal 920
zcmZQ(Qf6mhU}WH8;AN0ufB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z4D1Xn3=CkLo0ypglHg=uU=U+qU=Uzn28**ZFfcGPFf%YQFfbfq1gYg@aQBIK404Ju
zPAy0*N-Rl@FV09zNiB-cPs&P7E-417;R2~~_xE@Aag9$dD2OjEsmw`@&&kY7jZeue
zN=+`wEK3Ec<3LiEnU|bXnu4r=m4OB92AJDe8CV%W?#nMNan8@lF9Nxjm4Tgshk=2i
zC^au7waBrgBr!Q7H#M&WBFDlYz`(#zoS0hx<AeN`nHL%c;j=MFGcYhD<&>i6bZ1}z
z+am{3#K6D+;)Co^LgOnU@j><=%Y*nJc~H24^n>h0HV<SkvU(67q#hJ^!VFB{@Kywe
zJOh|5#=y+L#=yX!z`(#D&cMO|iWd+c6q^bREDRjrcwzvHTSNJvumZ_TGH^0*F))Dj
zgUp9I8%)9YATzkZA`D=)ATvOGkiS8~QVdKCd|>-o82CW~3=CjCNF9g|@-IkOnt_=?
zhyh{-$ekikdqLp<vO|;slwuheK;j_1AU-H;K<)skmw>7VrAv@J$PJ*d0hw*azzPlv
zkQ|5)^4kvvCI(gp1_oOO7O=Z{85qFn0mKKnR}h?*85qJCSi$ZRW?*0dsR4<D)PUR}
z4o&mn46F<w`z09|7(nVk;{FUA;4qb9U|;}=gD}W$kb6OXvSVNdrxlR=Uj{}73kF68
E00F^GY5)KL

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/quad_vert.spv b/projects/bloom/resources/shaders/quad_vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..c9c7a116497ce2098b9854b538d07fd99a77e383
GIT binary patch
literal 1076
zcmZQ(Qf6mhU}WH8;AK!?fB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z3>;uK3j+f~ZenI0h{MUiz`(=6z#z)Nz`)GF%)rFJz;K9>fq{jAlfm65-Z982zBsiY
zu_&=5HNH3_F(tJqK0hfdHMyi1q=pNm#@*lF-N!XPxu77vxTG>CH9jXZFEu_TvnVyW
zB(p3Pq>ckgU1nZ#PH76V0#*hV24pvZ_#k)XmzIQvF)%Q&GO&W(n3)%lUz}NznV$y|
zV`C6zU|>kki4RCE3QH|2Nv#0+hm8Ru4q}7kU>evM*um;SVFeb?%qt1btV#u`gUNyX
zo}Lr$oReALl384mn3tT&0FwiSB1o<@CkHOa%D@Z`H;@_b;1FYAkOOfT7#Ki&P`JYQ
z9t^Bt`$5v&AcBE`0VW1=6G#k1!^A*t0f~X^g^7XKATdx_xic_>%>$_uW?%xxn<6*{
z8Nh5Y24)5}1_p3C5ocgwU}s<e^Fitr7+An50c0LX+#1RUg*iwJq@D|^9>fQ!2iXg9
zA4nXe9>fRv4<rUs&kt1(a+@dv3xfdEZje{Cp?pCGQ0ir1kYQk95Q3@)iOE6Z3}yyM
z4unDWih$E41K6)1dqI3iS_P$976vh>Js@|0>;dt`L6$NwFo5`=FpvcE8|*>mf#L(?
zCy-rc46NX=0=XHa1SI~0fr){Yfq}u6frSC&S6*oT@MmBLhan#W0|Q75gh6^h_S!Kp
zGl0SlBnQ(c0o51CzzPm`DFy}xkQhi0OiUUo1~LOCCIb}%#Zv$SI|Im_vJ4CiApIcq
iAbF5~92r=_X-yuQcR_vzxdX%pssGEs$Y8<1$N&Ix4PBrB

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/shader.frag b/projects/bloom/resources/shaders/shader.frag
new file mode 100644
index 00000000..1c97a688
--- /dev/null
+++ b/projects/bloom/resources/shaders/shader.frag
@@ -0,0 +1,46 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 passNormal;
+layout(location = 1) in vec2 passUV;
+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 {
+    vec3 L; float padding;
+    mat4 lightMatrix;
+};
+layout(set=0, binding=3) uniform texture2D  shadowMap;
+layout(set=0, binding=4) uniform sampler    shadowMapSampler;
+
+float shadowTest(vec3 worldPos){
+    vec4 lightPos = lightMatrix * vec4(worldPos, 1);
+    lightPos /= lightPos.w;
+    lightPos.xy = lightPos.xy * 0.5 + 0.5;
+    
+    if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){
+        return 1;
+    }
+    
+    lightPos.z = clamp(lightPos.z, 0, 1);
+    
+    float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r;
+    float bias = 0.01f;
+    shadowMapSample += bias;
+    return shadowMapSample < lightPos.z ? 0 : 1;
+}
+
+void main()	{
+    /*
+    vec3 N = normalize(passNormal);
+    vec3 sunColor = vec3(1);
+    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;
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shader.vert b/projects/bloom/resources/shaders/shader.vert
new file mode 100644
index 00000000..0ab82c20
--- /dev/null
+++ b/projects/bloom/resources/shaders/shader.vert
@@ -0,0 +1,22 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec3 inNormal;
+layout(location = 2) in vec2 inUV;
+
+layout(location = 0) out vec3 passNormal;
+layout(location = 1) out vec2 passUV;
+layout(location = 2) out vec3 passPos;
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+    mat4 model;
+};
+
+void main()	{
+	gl_Position = mvp * vec4(inPosition, 1.0);
+	passNormal  = inNormal;
+    passUV      = inUV;
+    passPos     = (model * vec4(inPosition, 1)).xyz;
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shadow.frag b/projects/bloom/resources/shaders/shadow.frag
new file mode 100644
index 00000000..848f853f
--- /dev/null
+++ b/projects/bloom/resources/shaders/shadow.frag
@@ -0,0 +1,6 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+void main()	{
+
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shadow.vert b/projects/bloom/resources/shaders/shadow.vert
new file mode 100644
index 00000000..e0f41d42
--- /dev/null
+++ b/projects/bloom/resources/shaders/shadow.vert
@@ -0,0 +1,12 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 inPosition;
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+};
+
+void main()	{
+	gl_Position = mvp * vec4(inPosition, 1.0);
+}
\ No newline at end of file
diff --git a/projects/bloom/resources/shaders/shadow_frag.spv b/projects/bloom/resources/shaders/shadow_frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..6be3bd2518a3b1f234e39aea2503ba86cfb3314b
GIT binary patch
literal 288
zcmZQ(Qf6mhU}WH8;ALQAfB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z46F<+3=CkLo0ypgk`Q2E2J_h&7#Nrtm>HND7#I#Qg4A#_xckIA206tSrxqj@C6=Vd
z7iT1<q!z{JCuOB3mlT84aDmjg`}@25xW*?J6vP*oROY0{=Va!k#;0T!r6!kTmZgH!
zaUiM7%uCKGO+i)wauvv3FgFP^FoE5n2zC(zm~F<u3Z_BoK<)<l>IVZ811kdq!(RqQ
J1`7s81^|-UE#&|J

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/shadow_vert.spv b/projects/bloom/resources/shaders/shadow_vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..afaa0824ee9be2c22209d611943c6512587dce24
GIT binary patch
literal 1184
zcmZQ(Qf6mhU}WH8;AK!|fB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z4D4Vw3j+f~ZenI0h{Makz#z%Mz`)GF%)rFJz;K9>fq{jAlfm65-Z982zBsiYu_&=5
zHNH3_F(tJqK0hfdHMyi1q=pNm#@*lF-N!XPxu77vxTG>CH9jXZFEu_TvnVyWB(p3P
zq>ckgU1nZ#PH76V0#*hV24wfJGO#glGcYiu=fnr37KNo2m84dH+{p$N2eCnN`Nf$f
znfZAPYz*vR^`I~Si)ZGQ1ZP&Ig4Ds}K>kY4iFeM)EO5yzE=kNwPKE0Og$_urG$#ix
z$I8IW016k78LSMf41x>{49WR<Ae|tuGq5qRfW<)axn%_mAoao^4g&)NNIfV#GxGvq
zPGs<4U<JDoqy*$Q5DgOpxf>(~qG4hn_kzSgZi9(|*dQ@bSh+JWgUth}^FVS7D+2@A
zE|7accEHjI$PAbmC<O>GFff430EvOjaA#lvrvW*TCI$uuVFo5}T2KV13I;G+jDeYf
zje&tdfq{WRoPh<Ll0bZrdqf#n7&xGEAS1M)d`<>XYGq)MVPIk4VqgI42Z_l+;}YZ+
zkQpF35LRGdVc-F$MNoQXV6cYrL2&`{Kgb?Fs6C+Y0@(xN^Mfp9U|^77U||6H4P-9J
zU2+V};CKRwfy@?SU|;~*5Arw2Y!Dw5Zy+&{ya-etq#oo)aj2U?c7x<4p#A}ggY<*=
zpty#)1>_fLuv;4JLFyHuegV11jDZy#jv#k|lz`0q!N9}-iZe$BRtAvWqTsM+U;z2Y
z7U~x<1_lO@IEW9jR|=Y60~tU$1r)Xn3?MO(nJ_U~28bBQT`)0F+=A4B;yZwW9UOo1
z3=sVw^&okW+b1%xg5^N*46+-f2IM}FUz8Xa!1)5?K05|x1`r>l?k@u)g9QU40|0-Z
BV|)Mr

literal 0
HcmV?d00001

diff --git a/projects/bloom/resources/shaders/vert.spv b/projects/bloom/resources/shaders/vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..5e514eef5983927316465679af5461f507497130
GIT binary patch
literal 1872
zcmZQ(Qf6mhU}WH8;AOC2fB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z3_M^q3j+f~ZenI0h{Makz#z%Mz@Wmwz@W;&z@W*%z@W{*z@W>(z`)GF%)rFJz;K9>
zfq{jAlfm65-Z982zBsiYu_&=5HNH3_F(tJqK0hfdHMyi1q=pNm#@*lF-N!XPxu77v
zxTG>CH9jXZFEu_TvnVyWB(p3Pq>ckgU1nZ#PH76V0#*hV24pv~GO#glGcYiu=fnr3
z7KNo2m84dH+|C9S2eCnN`Nf$fnfZAPYz*vR^`LM8i)ZGQ1ZP&Ig4Ds}K>kY4iFeM)
zEO5yzE=kNwPKE0Og&0V#G$#ix$I8IW017LR8LSMf41x>{49WR<Ae|tuGq5qRfW<)a
zxn%_mY)~~IH{|B0q~<U%Fo4Vu1}OluS;66+nHK<aHUr39P&^bQ78m>F7v(1Afb_6}
z<0Uf>CIS*;0ml<aU1%7D4~nzQJP;4U2gM&qJ|MrC!GnPn93CL6LE#9ZVPYWnfy6*G
zObiq@ATdxlz{EgokQgXl+!>g`=7H3CAo-h>fdOn6C~QD>D1!(zF?I$91_7`bB)mZO
zBI^V3LHZoPY9QeZ5`*aj*$pxuWVbs53pfnrp!R_HpzuTHgVHpJ4>AKJ4@%$2d{DT7
z_%QQ8;RfRiGcbYEzals%Fo4-&49pB{3=9kk3=9n73@i*FcY*kza1mu-Vc>wuft;ib
z<#RHCaxDXc3<C=T7Xt%WKS)dtntowsfaE|}fq?~_Q(<9l4dsK<A4m*j4<FPXkiS6o
zfcX3%OBom#Bp6u0VGJ@C<SsdA9D%|eghA#DF)%QI+yDwokoh1!C~bnoK=L9`d60UL
zKgFT$2H6jimw@^SBo5LK;)BW&n0r8BA`Ny=gFQ&SBDg$Z0EZWd4~k=uSup(|J}B-$
zVleYSaR)LFWWPGpeo#Dt^lL!P1BrvwgZQAh1?dOLYeC%ylLzrZaSbvLq#k4+D6T>3
zVd19-)nmrM3XW?~*nzwNQuBj>i2;;O9T`{|Kw%@wz`y_sFHo4<Lc?2(fq?-e4&sCS
zB?T>80~tUi7%2W37(ikmGht$~3=lDpyI^9V^aoN0@@oJCJ2=eb86f&W>Ou0Lu${=j
z3YG(<MUdSfH6Zta!di)e0bE{z+-JwY%mCtp)WO`Q1}*PFa-eVr@j-H+@X~?S0U$Y0
zn1c8qb3y*rhx!{7Rv`7D^Z}}9K;j@i$lr!&{x)J@U;v3B``Z{QhU{+>s2H-pO&J&%
zK>9)ILGsA{He+C50ND*v<Iljs0J7Vhfq?-e4#FV&K<0zY1Nj9M-yr$F42%pG42%o_
DN8gGX

literal 0
HcmV?d00001

diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp
new file mode 100644
index 00000000..e33f603b
--- /dev/null
+++ b/projects/bloom/src/main.cpp
@@ -0,0 +1,428 @@
+#include <iostream>
+#include <vkcv/Core.hpp>
+#include <GLFW/glfw3.h>
+#include <vkcv/camera/CameraManager.hpp>
+#include <chrono>
+#include <vkcv/asset/asset_loader.hpp>
+
+int main(int argc, const char** argv) {
+	const char* applicationName = "First Mesh";
+
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
+
+	vkcv::Window window = vkcv::Window::create(
+		applicationName,
+		windowWidth,
+		windowHeight,
+		true
+	);
+
+	vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
+	cameraManager.getCamera().setPosition(glm::vec3(0.f, 0.f, 3.f));
+	cameraManager.getCamera().setNearFar(0.1, 30);
+
+	window.initEvents();
+
+	vkcv::Core core = vkcv::Core::create(
+		window,
+		applicationName,
+		VK_MAKE_VERSION(0, 0, 1),
+		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
+		{},
+		{ "VK_KHR_swapchain" }
+	);
+
+
+    // 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
+    );
+
+    const vkcv::AttachmentDescription depthAttachment(
+            vkcv::AttachmentOperation::STORE,
+            vkcv::AttachmentOperation::CLEAR,
+            vk::Format::eD32Sfloat
+    );
+
+    const vkcv::AttachmentDescription presentAttachment(
+            vkcv::AttachmentOperation::STORE,
+            vkcv::AttachmentOperation::CLEAR,
+            core.getSwapchainImageFormat()
+    );
+
+    // RENDER PASSES
+    const vkcv::PassConfig shadowPassConfig({shadowAttachment});
+    const vkcv::PassHandle shadowPassHandle = core.createPass(shadowPassConfig);
+
+    const vkcv::PassConfig meshPassDefinition({ offscreenAttachment, depthAttachment });
+    const vkcv::PassHandle meshPassHandle = core.createPass(meshPassDefinition);
+
+    const vkcv::PassConfig screenQuadPassConfig({presentAttachment});
+    const vkcv::PassHandle screenQuadPassHandle = core.createPass(screenQuadPassConfig);
+
+    if (!shadowPassHandle || !meshPassHandle || !screenQuadPassHandle)
+    {
+        std::cout << "Error. Could not create render passes. Exiting." << std::endl;
+        return EXIT_FAILURE;
+    }
+
+
+    // SHADERS
+    vkcv::ShaderProgram shadowProgram;
+    shadowProgram.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv");
+    shadowProgram.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv");
+
+    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"));
+
+    vkcv::ShaderProgram bloomComputeProgram{};
+    bloomComputeProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/blur_comp.spv"));
+
+    vkcv::ShaderProgram screenQuadProgram{};
+    screenQuadProgram.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/quad_vert.spv");
+    screenQuadProgram.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/quad_frag.spv");
+
+
+    vkcv::asset::Mesh mesh;
+
+	const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
+	int result = vkcv::asset::loadMesh(path, mesh);
+
+	if (result == 1) {
+		std::cout << "Mesh loading successful!" << std::endl;
+	}
+	else {
+		std::cout << "Mesh loading failed: " << result << std::endl;
+		return 1;
+	}
+	assert(mesh.vertexGroups.size() > 0);
+
+    auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
+
+    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);
+    });
+
+    // 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);
+
+	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,
+		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)
+	};
+
+	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::DrawcallInfo quadDrawcall(loadedQuad, {screenQuadDescriptorUsage});
+
+	modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f));
+
+	std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
+	std::vector<glm::mat4> mvpLight;
+
+
+	auto start = std::chrono::system_clock::now();
+	const auto appStartTime = start;
+    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();
+
+			windowWidth = swapchainWidth;
+			windowHeight = swapchainHeight;
+		}
+
+		auto end = std::chrono::system_clock::now();
+		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+		start = end;
+		cameraManager.getCamera().updateView(deltatime.count() * 0.000001);
+
+		const float sunTheta = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime).count() * 0.001f;
+		lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta)));
+
+		const float shadowProjectionSize = 5.f;
+		glm::mat4 projectionLight = glm::ortho(
+			-shadowProjectionSize,
+			shadowProjectionSize,
+			-shadowProjectionSize,
+			shadowProjectionSize,
+			-shadowProjectionSize,
+			shadowProjectionSize);
+
+		glm::mat4 vulkanCorrectionMatrix(1.f);
+		vulkanCorrectionMatrix[2][2] = 0.5;
+		vulkanCorrectionMatrix[3][2] = 0.5;
+		projectionLight = vulkanCorrectionMatrix * projectionLight;
+
+		const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0));
+
+		lightInfo.lightMatrix = projectionLight * viewLight;
+		lightBuffer.fill({ lightInfo });
+
+		const glm::mat4 viewProjectionCamera = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
+
+		mainPassMatrices.clear();
+		mvpLight.clear();
+		for (const auto& m : modelMatrices) {
+			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
+			mvpLight.push_back(lightInfo.lightMatrix* m);
+		}
+
+		vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
+
+		vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+
+		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
+
+		// shadow map pass
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+			shadowPassHandle,
+			shadowPipelineHandle,
+			shadowPushConstantData,
+			shadowDrawcalls,
+			{ shadowMapHandle });
+
+		core.prepareImageForSampling(cmdStream, shadowMapHandle);
+
+        // offscreen render pass
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+            meshPassHandle,
+            meshPipelineHandle,
+			pushConstantData,
+			meshDrawcalls,
+            {offscreenImage.getHandle(), depthBufferHandle});
+
+
+		core.prepareImageForSampling(cmdStream, offscreenImage.getHandle());
+		//core.prepareImageForStorage(cmdStream, bloomAttachment.getHandle());
+
+		// compute blur pass
+		const std::array<uint32_t, 3> dispatchCount = {windowWidth, windowHeight, 0};
+		/*
+		core.recordComputeDispatchToCmdStream(
+		        cmdStream,
+		        bloomComputePipeline,
+		        dispatchCount.data(),
+                {bloomSetUsage},
+                vkcv::PushConstantData(nullptr, 0));
+        */
+		// final compositing screen quad pass
+		core.recordDrawcallsToCmdStream(
+		        cmdStream,
+		        screenQuadPassHandle,
+		        screenQuadPipelineHandle,
+		        vkcv::PushConstantData(nullptr, 0),
+                {quadDrawcall},
+                {swapchainHandle}
+		        );
+
+		core.prepareSwapchainImageForPresent(cmdStream);
+		core.submitCommandStream(cmdStream);
+
+		core.endFrame();
+	}
+
+	return 0;
+}
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 44e7111e..64900670 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -507,4 +507,10 @@ namespace vkcv
 			m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer);
 		}, nullptr);
 	}
+
+    void Core::prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image) {
+        recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
+            m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, cmdBuffer);
+        }, nullptr);
+    }
 }
-- 
GitLab