From d85734c7497cbd421aae16ea13f75c3fb14f504f Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Sun, 6 Jun 2021 20:23:14 +0200
Subject: [PATCH] [#66] Added barrier/transition function for sampled images
 and implemented shadow mapping as a test

---
 include/vkcv/Core.hpp                         |   1 +
 include/vkcv/PipelineConfig.hpp               |   2 +-
 include/vkcv/ShaderProgram.hpp                |   2 +
 .../resources/shaders/compile.bat             |   2 +
 .../cmd_sync_test/resources/shaders/frag.spv  | Bin 920 -> 3844 bytes
 .../resources/shaders/shader.frag             |  32 ++++-
 .../resources/shaders/shader.vert             |   3 +
 .../resources/shaders/shadow.frag             |   6 +
 .../resources/shaders/shadow.vert             |  12 ++
 .../resources/shaders/shadow_frag.spv         | Bin 0 -> 288 bytes
 .../resources/shaders/shadow_vert.spv         | Bin 0 -> 1184 bytes
 .../cmd_sync_test/resources/shaders/vert.spv  | Bin 1520 -> 1872 bytes
 projects/cmd_sync_test/src/main.cpp           | 120 ++++++++++++++++--
 src/vkcv/Core.cpp                             |  10 +-
 src/vkcv/PipelineConfig.cpp                   |  14 +-
 src/vkcv/PipelineManager.cpp                  |   2 +-
 src/vkcv/ShaderProgram.cpp                    |  11 ++
 17 files changed, 193 insertions(+), 24 deletions(-)
 create mode 100644 projects/cmd_sync_test/resources/shaders/shadow.frag
 create mode 100644 projects/cmd_sync_test/resources/shaders/shadow.vert
 create mode 100644 projects/cmd_sync_test/resources/shaders/shadow_frag.spv
 create mode 100644 projects/cmd_sync_test/resources/shaders/shadow_vert.spv

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 01e59191..8a165adf 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -258,5 +258,6 @@ namespace vkcv
 
 		void submitCommandStream(const CommandStreamHandle handle);
 		void prepareSwapchainImageForPresent(const CommandStreamHandle handle);
+		void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image);
     };
 }
diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp
index f464997b..7dc6f220 100644
--- a/include/vkcv/PipelineConfig.hpp
+++ b/include/vkcv/PipelineConfig.hpp
@@ -27,7 +27,7 @@ namespace vkcv {
             const ShaderProgram&                        shaderProgram,
             uint32_t                                    width,
             uint32_t                                    height,
-            PassHandle                                  &passHandle,
+            const PassHandle                            &passHandle,
             const std::vector<VertexAttribute>          &vertexAttributes,
             const std::vector<vk::DescriptorSetLayout>  &descriptorLayouts,
             bool                                        useDynamicViewport);
diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp
index ef5d1f00..459125bb 100644
--- a/include/vkcv/ShaderProgram.hpp
+++ b/include/vkcv/ShaderProgram.hpp
@@ -58,10 +58,12 @@ namespace vkcv {
         void reflectShader(ShaderStage shaderStage);
 
         const VertexLayout &getVertexLayout() const;
+		size_t getPushConstantSize() const;
 
 	private:
         std::unordered_map<ShaderStage, Shader> m_Shaders;
 
         VertexLayout m_VertexLayout;
+		size_t m_pushConstantSize = 0;
 	};
 }
diff --git a/projects/cmd_sync_test/resources/shaders/compile.bat b/projects/cmd_sync_test/resources/shaders/compile.bat
index b4521235..516c2f2f 100644
--- a/projects/cmd_sync_test/resources/shaders/compile.bat
+++ b/projects/cmd_sync_test/resources/shaders/compile.bat
@@ -1,3 +1,5 @@
 %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
 pause
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/frag.spv b/projects/cmd_sync_test/resources/shaders/frag.spv
index 087e4e22fb2fcec27d99b3ff2aa1a705fe755796..ff3110571871d65ce119dc6c5006e7e67aa53546 100644
GIT binary patch
literal 3844
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
JL2OWY2mp}OH~s(s

literal 920
zcmZQ(Qf6mhU}WH8;AN0yfB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z3>*wB3=CkLo0ypglHg=uU=U+qV31*8U=Uzn2FtNCFfcGPFf%YQFfbfq1gYm_aQBIK
z404JuPAy0*N-Rl@FV09zNiB-cPs&P7E-417;R2~~_xE@Aag9$dD2OjEsmw`@&&kY7
zjZeueN=+`wEK3Ec<3LiEnU|bXnu4r=m4OB92AKO;8CV%WZp<$&an8@lF9Nw4B*w$Q
zz>u3-oDq^*QBqoz%D~FN#vs7Jz)%9=2Pfth<fIlcFo4Vh`L7_cxHvQn#AgNj6~y<;
zFUn2KVPJ4)U;*192Qr)i!UvhJgvM7y;)Co$mIv`c@}Rf?nFq2HWFCwU3SST(WP~sS
z6F6)Y!J*6mW{WW}Gq5o*FeorEFo-j-Fo5C!#0SNm0s{*J2RKd`z~a_WJ}6v3@{$ak
z3|tHhVErKbKz6_|j1Mw{8!8A=3o--52l*2uEXBaYzz4RUg@GR=z`y|JgVcfeApe1c
zr5TtRgcumW_JQ0f0<{;ERzP-$GJsMj0|Q7Lq!+{og$2kRAoUVZ^`LYJQZEfv5ArWa
zJ%|qqE0B6K23By`fYgHQ2Kn^|0}}%)0|SFC0}I#<ybKHsAU}ZkAomM`(=h`>7y~QV
zox%(Z3?MZiagZ92yTqYsKAeG-0puP@1_lO@I*_<O0|z*ar5G3(K;j?_vK!<ckRR<B
Sn89fUB>$Izk->t2kpTei7*62;

diff --git a/projects/cmd_sync_test/resources/shaders/shader.frag b/projects/cmd_sync_test/resources/shaders/shader.frag
index 71a1de69..95f1b331 100644
--- a/projects/cmd_sync_test/resources/shaders/shader.frag
+++ b/projects/cmd_sync_test/resources/shaders/shader.frag
@@ -3,12 +3,42 @@
 
 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()	{
-	outColor = texture(sampler2D(meshTexture, textureSampler), passUV).rgb;
+    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 = albedo * (sun + ambient);
 }
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/shader.vert b/projects/cmd_sync_test/resources/shaders/shader.vert
index 76855152..0ab82c20 100644
--- a/projects/cmd_sync_test/resources/shaders/shader.vert
+++ b/projects/cmd_sync_test/resources/shaders/shader.vert
@@ -7,13 +7,16 @@ 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/cmd_sync_test/resources/shaders/shadow.frag b/projects/cmd_sync_test/resources/shaders/shadow.frag
new file mode 100644
index 00000000..848f853f
--- /dev/null
+++ b/projects/cmd_sync_test/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/cmd_sync_test/resources/shaders/shadow.vert b/projects/cmd_sync_test/resources/shaders/shadow.vert
new file mode 100644
index 00000000..e0f41d42
--- /dev/null
+++ b/projects/cmd_sync_test/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/cmd_sync_test/resources/shaders/shadow_frag.spv b/projects/cmd_sync_test/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/cmd_sync_test/resources/shaders/shadow_vert.spv b/projects/cmd_sync_test/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/cmd_sync_test/resources/shaders/vert.spv b/projects/cmd_sync_test/resources/shaders/vert.spv
index 374c023e14b351eb43cbcda5951cbb8b3d6f96a1..5e514eef5983927316465679af5461f507497130 100644
GIT binary patch
delta 394
zcmeyseSwdcnMs+Qfq{{Mi-DKHVj{08BhN%<O-9{~nf8prYz(Xnf(#&828P`Hl++vs
zhRMqrGx=E=SQvB}7#Io?i;DyDizo9lP1W^aV1cV;Wnf_NK+>zsz`)>uEH-&2Q#z|V
z$dt)nnXa-!bP8-f%goLwY0bdG0MY=mfSG}TL7RbvfscWKL2vU}7HP(MM+Q~~5e5bZ
zeFg>wVUREb1A{FC3xfay1A_rn9K;uAU|=w0U|^7BU|<MjU}a!qU|=wUiZLLG8AHVw
zk;F`(VoVGS3;_)64D1XH45kqMp$s4mAO+G43=9()SizEJP|HDz{24eHK$e>`Fff3`
cK^SBq$O4dob_~qmzyrzuWng5mU|?hb08{8ANB{r;

delta 69
zcmcb>_ko+2nMs+Qfq{{Mi-DIxcOtJTBlkpS&5Z>%jFS&AW=ytVnlhP%Ic9PJ^C@O#
a2EomaEbNS%Ggw6!IsP&*GFUJ$G5`P-)(-;!

diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp
index c3455be6..d28adb37 100644
--- a/projects/cmd_sync_test/src/main.cpp
+++ b/projects/cmd_sync_test/src/main.cpp
@@ -20,6 +20,7 @@ int main(int argc, const char** argv) {
 
 	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();
 
@@ -103,8 +104,11 @@ int main(int argc, const char** argv) {
 	triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT);
 	
 	std::vector<vkcv::DescriptorBinding> descriptorBindings = {
-		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,	1, vkcv::ShaderStage::FRAGMENT),
-		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,			1, vkcv::ShaderStage::FRAGMENT)};
+		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,    1, vkcv::ShaderStage::FRAGMENT),
+		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,          1, vkcv::ShaderStage::FRAGMENT),
+		vkcv::DescriptorBinding(vkcv::DescriptorType::UNIFORM_BUFFER,   1, vkcv::ShaderStage::FRAGMENT),
+		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED,    1, vkcv::ShaderStage::FRAGMENT) ,
+		vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER,          1, vkcv::ShaderStage::FRAGMENT) };
 	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings);
 
 	const vkcv::PipelineConfig trianglePipelineDefinition(
@@ -132,10 +136,12 @@ int main(int argc, const char** argv) {
 		vkcv::SamplerAddressMode::REPEAT
 	);
 
-	vkcv::DescriptorWrites setWrites;
-	setWrites.sampledImageWrites	= { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
-	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(1, sampler) };
-	core.writeResourceDescription(descriptorSet, 0, setWrites);
+    vkcv::SamplerHandle shadowSampler = core.createSampler(
+        vkcv::SamplerFilterType::NEAREST,
+        vkcv::SamplerFilterType::NEAREST,
+        vkcv::SamplerMipmapMode::NEAREST,
+        vkcv::SamplerAddressMode::CLAMP_TO_EDGE
+    );
 
 	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle();
 
@@ -147,19 +153,69 @@ int main(int argc, const char** argv) {
 		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,  2.f, 0.f),
+		glm::vec3( 0.f, -5.f, 0.f)
 	};
 
 	std::vector<glm::mat4> modelMatrices;
 	std::vector<vkcv::DrawcallInfo> drawcalls;
+	std::vector<vkcv::DrawcallInfo> shadowDrawcalls;
 	for (const auto& position : instancePositions) {
 		modelMatrices.push_back(glm::translate(glm::mat4(1.f), position));
 		drawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { descriptorUsage }));
+		shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {}));
 	}
 
-	std::vector<glm::mat4> mvpMatrices;
+	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;
+
+	vkcv::ShaderProgram shadowShader;
+	shadowShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv");
+	shadowShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv");
+    shadowShader.reflectShader(vkcv::ShaderStage::VERTEX);
+    shadowShader.reflectShader(vkcv::ShaderStage::FRAGMENT);
+
+	const vk::Format shadowMapFormat = vk::Format::eD16Unorm;
+	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 uint32_t shadowMapResolution = 1024;
+	const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1);
+	const vkcv::PipelineConfig shadowPipeConfig(
+		shadowShader, 
+		shadowMapResolution, 
+		shadowMapResolution, 
+		shadowPass, 
+		attributes,
+		{}, 
+		false);
+	const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig);
+
+	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::DescriptorWrites setWrites;
+	setWrites.sampledImageWrites    = { 
+        vkcv::SampledImageDescriptorWrite(0, texture.getHandle()),
+        vkcv::SampledImageDescriptorWrite(3, shadowMap.getHandle()) };
+	setWrites.samplerWrites         = { 
+        vkcv::SamplerDescriptorWrite(1, sampler), 
+        vkcv::SamplerDescriptorWrite(4, shadowSampler) };
+    setWrites.uniformBufferWrites   = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) };
+	core.writeResourceDescription(descriptorSet, 0, setWrites);
 
 	auto start = std::chrono::system_clock::now();
+	const auto appStartTime = start;
 	while (window.isWindowOpen()) {
 		vkcv::Window::pollEvents();
 		
@@ -180,16 +236,54 @@ int main(int argc, const char** argv) {
 		start = end;
 		cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count());
 
-		const glm::mat4 viewProjection = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
-		mvpMatrices.clear();
+		const float sunTheta = (end - appStartTime).count() * 0.0000001;
+		lightInfo.direction = glm::normalize(glm::vec3(cos(sunTheta), 1, 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) {
-			mvpMatrices.push_back(viewProjection * m);
+			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
+			mvpLight.push_back(lightInfo.lightMatrix* m);
 		}
-		vkcv::PushConstantData pushConstantData((void*)mvpMatrices.data(), sizeof(glm::mat4));
-        const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
+
+		vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
+		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
+
+		vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+			shadowPass,
+			shadowPipe,
+			shadowPushConstantData,
+			shadowDrawcalls,
+			{ shadowMap.getHandle() });
+
+		core.prepareImageForSampling(cmdStream, shadowMap.getHandle());
+
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
 			trianglePass,
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 908cb617..9ed83d2a 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -375,7 +375,9 @@ namespace vkcv
 		const FinishCommandFunction &finish) {
 
 		m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record);
-		m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish);
+		if (finish) {
+			m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish);
+		}
 	}
 
 	void Core::submitCommandStream(const CommandStreamHandle handle) {
@@ -457,4 +459,10 @@ namespace vkcv
 			recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::ePresentSrcKHR);
 		});
 	}
+
+	void Core::prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image) {
+		recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
+			m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer);
+		}, nullptr);
+	}
 }
diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp
index 14259245..ad8437ca 100644
--- a/src/vkcv/PipelineConfig.cpp
+++ b/src/vkcv/PipelineConfig.cpp
@@ -9,13 +9,13 @@
 namespace vkcv {
 
     PipelineConfig::PipelineConfig(
-		const ShaderProgram&						shaderProgram,
-		uint32_t									width,
-		uint32_t									height,
-		PassHandle									&passHandle,
-		const std::vector<VertexAttribute>			&vertexAttributes,
-		const std::vector<vk::DescriptorSetLayout>	&descriptorLayouts,
-		bool										useDynamicViewport)
+		const ShaderProgram&                        shaderProgram,
+		uint32_t                                    width,
+		uint32_t                                    height,
+		const PassHandle                            &passHandle,
+		const std::vector<VertexAttribute>          &vertexAttributes,
+		const std::vector<vk::DescriptorSetLayout>  &descriptorLayouts,
+		bool                                        useDynamicViewport)
 		:
 		m_ShaderProgram(shaderProgram),
 		m_Height(height),
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
index 9afa2bc9..28a64a24 100644
--- a/src/vkcv/PipelineManager.cpp
+++ b/src/vkcv/PipelineManager.cpp
@@ -174,7 +174,7 @@ namespace vkcv
                 { 1.f,1.f,1.f,1.f }
         );
 
-		const size_t matrixPushConstantSize = 4 * 4 * sizeof(float);
+		const size_t matrixPushConstantSize = config.m_ShaderProgram.getPushConstantSize();
 		const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, matrixPushConstantSize);
 
         // pipeline layout
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 5185b8b4..3eea7ed2 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "vkcv/ShaderProgram.hpp"
+#include <algorithm>
 
 namespace vkcv {
     /**
@@ -128,9 +129,19 @@ namespace vkcv {
 
 			m_VertexLayout = VertexLayout(inputVec);
 		}
+		for (const auto &pushConstantBuffer : resources.push_constant_buffers) {
+			for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id)) {
+				const size_t size = range.range + range.offset;
+				m_pushConstantSize = std::max(m_pushConstantSize, size);
+			}
+		}
     }
 
     const VertexLayout& ShaderProgram::getVertexLayout() const{
         return m_VertexLayout;
 	}
+
+	size_t ShaderProgram::getPushConstantSize() const {
+		return m_pushConstantSize;
+	}
 }
-- 
GitLab