From 66682abe8c2733a43ef717b695b49816e922ee17 Mon Sep 17 00:00:00 2001 From: saschawillems Date: Fri, 14 Oct 2016 20:35:41 +0200 Subject: [PATCH] Sub pass G-Buffer compositing example (wip) --- data/shaders/subpasses/composition.frag | 81 ++ data/shaders/subpasses/composition.frag.spv | Bin 0 -> 4264 bytes data/shaders/subpasses/composition.vert | 17 + data/shaders/subpasses/composition.vert.spv | Bin 0 -> 1028 bytes data/shaders/subpasses/gbuffer.frag | 30 + data/shaders/subpasses/gbuffer.frag.spv | Bin 0 -> 1576 bytes data/shaders/subpasses/gbuffer.vert | 42 + data/shaders/subpasses/gbuffer.vert.spv | Bin 0 -> 2448 bytes subpasses/subpasses.cpp | 940 ++++++++++++++++++++ subpasses/subpasses.vcxproj | 101 +++ subpasses/subpasses.vcxproj.filters | 59 ++ vulkanExamples.sln | 6 + 12 files changed, 1276 insertions(+) create mode 100644 data/shaders/subpasses/composition.frag create mode 100644 data/shaders/subpasses/composition.frag.spv create mode 100644 data/shaders/subpasses/composition.vert create mode 100644 data/shaders/subpasses/composition.vert.spv create mode 100644 data/shaders/subpasses/gbuffer.frag create mode 100644 data/shaders/subpasses/gbuffer.frag.spv create mode 100644 data/shaders/subpasses/gbuffer.vert create mode 100644 data/shaders/subpasses/gbuffer.vert.spv create mode 100644 subpasses/subpasses.cpp create mode 100644 subpasses/subpasses.vcxproj create mode 100644 subpasses/subpasses.vcxproj.filters diff --git a/data/shaders/subpasses/composition.frag b/data/shaders/subpasses/composition.frag new file mode 100644 index 00000000..48fcee68 --- /dev/null +++ b/data/shaders/subpasses/composition.frag @@ -0,0 +1,81 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (input_attachment_index = 1, binding = 0) uniform subpassInput samplerposition; +layout (input_attachment_index = 2, binding = 1) uniform subpassInput samplerNormal; +layout (input_attachment_index = 3, binding = 2) uniform subpassInput samplerAlbedo; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragcolor; +layout (location = 1) out vec4 outPosition; +layout (location = 2) out vec4 outNormal; +layout (location = 3) out vec4 outAlbedo; + +layout (constant_id = 0) const int NUM_LIGHTS = 32; + +struct Light { + vec4 position; + vec3 color; + float radius; +}; + +layout (binding = 3) uniform UBO +{ + vec4 viewPos; + Light lights[NUM_LIGHTS]; +} ubo; + + +void main() +{ + // Read G-Buffer values from previous sub pass + vec3 fragPos = subpassLoad(samplerposition).rgb; + vec3 normal = subpassLoad(samplerNormal).rgb; + vec4 albedo = subpassLoad(samplerAlbedo); + + #define ambient 0.15 + + // Ambient part + vec3 fragcolor = albedo.rgb * ambient; + + for(int i = 0; i < NUM_LIGHTS; ++i) + { + // Vector to light + vec3 L = ubo.lights[i].position.xyz - fragPos; + // Distance from light to fragment position + float dist = length(L); + + // Viewer to fragment + vec3 V = ubo.viewPos.xyz - fragPos; + V = normalize(V); + + // Light to fragment + L = normalize(L); + + // Attenuation + float atten = ubo.lights[i].radius / (pow(dist, 2.0) + 1.0); + + // Diffuse part + vec3 N = normalize(normal); + float NdotL = max(0.0, dot(N, L)); + vec3 diff = ubo.lights[i].color * albedo.rgb * NdotL * atten; + + // Specular part + // Specular map values are stored in alpha of albedo mrt + vec3 R = reflect(-L, N); + float NdotR = max(0.0, dot(R, V)); + vec3 spec = ubo.lights[i].color * albedo.a * pow(NdotR, 32.0) * atten; + + fragcolor += diff + spec; + } + + outFragcolor = vec4(fragcolor, 1.0); + + // Write G-Buffer attachments to avoid undefined behaviour (validation error) + outPosition = vec4(0.0); + outNormal = vec4(0.0); + outAlbedo = vec4(0.0); +} \ No newline at end of file diff --git a/data/shaders/subpasses/composition.frag.spv b/data/shaders/subpasses/composition.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..016eae6eccfedff96ce51498d372da3e09057c97 GIT binary patch literal 4264 zcmZ9NiFQ;)5QZ-#6ImjQY$6B&TtFfsvMCZsRDu%O7X>v;k|7KvnUI+T6_p^OxPS|b z!dv(XK9om2$8-F?n_JM*bN*9Re^pmkS9jlOn!e_-EStt}Ms`1&8uPR1*jc7ri=3Uc zsVzPZ_~tQlPlm#ZW4H^41$2iyhsz&z#{!=h+StdM1Y zuvl|8Gwbf@?L6GoTd$0k$I6XLZ~bg}pfc85>pNHJZ`A)EgR43+*gI4n85}PUR(dyY z+BjP7KcAJzl-8giAo-R_uCtcyy>THd?D!8`auKK5r5HNNsGmJe1{e ziyy}AFX%jO8GL!DuQE`Zid+6LuCt(tD`hL;UAMnBR2xGtvEOHr)k1#>*^}$dnGNl! z4xViwTlnP-Mt@LX&X;oDvx%H7WHr`4j?t5G`s6yBu#a^eAX6#VF|&zk<)S?Z&OAe| zSFMiMswDIK2WV&PM)t5c|6yfvwg^NqfzmYUF!sLf8jf+K2U4KzD{EgRL`72 zYB%I_3BTZywP<#!A&Ij2&7=irhv zY0mFpb>x^^$-3ERD{{34BkWaE<^y{@w!PBtok-)gjZgi?dmhGnH_~`*<5R!!z9i$@ zk>+yjWv_7$)=W_kIAi4IC2{7Ia}9I7jBEjIz+B!5N1976&DCApov6<{k=C!h3N!)N z@Qtqk4sGANwmG$Z``VH3ZehoKzX>sa0a3=SBD;R|#b~jICFJwFp{>6Sdj?WYEaj!o zLh2XkFqgwQv!~QNc1K}*4;+iI=OEjFdstuCbI~2vV9$*)XFGk>o)3%>#yo+v2ERqI zhIhqb4NoDh*|_j~ZVuOU?rL!V-qH@^pFua?bvI!vy(?{CA(gy{Tv6N?a~f-o<-~dq zyhp~b!1m0IkvI1%NcSRdE!v*5TvezH-dnk_y|>!! ztYX}4U=1lE);pcf^-gPh?!O}4r}et_Z^&st-uc?zdAY5emvP<^hdKU&DItyZZu{Tm zzQguEDs1o2vcxvt|D5o9XZ*hjTfhG~VS8>JiLJjgvGwmuZ2f-g)A=V8TmR`t?4iUq z--X0Bexk57-qrwPRs-{S zo^tA*rQeso+3j;k?~3wI)>?~P2ORoeKM?gKx^p|JavO3x*bMwG=(Dca^Q-9kW6!Ul%Lzy9*?9N719)y%iypj=?i_jd?VXj2 z_+997t-Lhf+HPRZE}+jj-s|0u;< z(@43fa~R$JTxJc|9RYITuc6ydd!>IASZg;hR=;_NkoscY7`j~8b@WaU=g>emMn2AA z9NpY;4#t@K0#cv3Jco-QJ%^96;~ey*=kN)(e&6y=P9Wusx6jwW>BRmT-5B4K{%?RW z@-gQ+`brQn-=fPI>l*sx{Kl_C`khuf-@bka?CT_JXn$XD_S5geD{x% zg8z~)1&K5&5Fs_s40mXGia)AuhoCmSJSMuy1ubS z#;_^7L~=&-9jjM3jXE*$fbddyE0pysX}?i&uTE<9aOdD>nA)?b8)Y_3kD`|ChRL75 z_ApDI=IE#sx5JYtZl6bO8?Jv@JBtpFO+`+R>%pm+oM*qA+AHdt`t6gjX}g)d*o#~C zBG=#Kdc$_}BT;2n4LVETG?KKFb&^;Q8B>A7(qRD)f#o?a_PFMP%n<5PSyt{Cm{ zHxuIM4f*mGlL9{I-_;#&f`%vOvjW<+&*y&zjd>Ivfnw$Xu6*8tVsZmvUm7!96wq0P m_CYaPkI+y>X0{}d%NcXUcSjz~by?u8cpv(OlmA~1Tf#l@0!YFD literal 0 HcmV?d00001 diff --git a/data/shaders/subpasses/gbuffer.frag b/data/shaders/subpasses/gbuffer.frag new file mode 100644 index 00000000..dee43e80 --- /dev/null +++ b/data/shaders/subpasses/gbuffer.frag @@ -0,0 +1,30 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 1) uniform sampler2D samplerColor; +layout (binding = 2) uniform sampler2D samplerNormalMap; + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec3 inWorldPos; + +layout (location = 0) out vec4 outColor; +layout (location = 1) out vec4 outPosition; +layout (location = 2) out vec4 outNormal; +layout (location = 3) out vec4 outAlbedo; + +void main() +{ + outPosition = vec4(inWorldPos, 1.0); + + vec3 N = normalize(inNormal); + N.y = -N.y; + outNormal = vec4(N, 1.0); + + outAlbedo = vec4(inColor, 1.0); + + // Write color attachments to avoid undefined behaviour (validation error) + outColor = vec4(0.0); +} \ No newline at end of file diff --git a/data/shaders/subpasses/gbuffer.frag.spv b/data/shaders/subpasses/gbuffer.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..3fad360abf08e872d8f12840348e6bbff42f53fb GIT binary patch literal 1576 zcmY+DTW`}q5QVqRrG&ooToO3h!&AX19jukg6-?;@>&*xtMbHANU2JO!C0~m|0 zk`v)9xrVE1#zuY^6SyR6$kt?cWcOsdvVGaIa^Y%Itt#5dxnC+&RaUR>bw9oHvS93| z{v`0Sul^uNz2x*;a5l;Q=O`4$Lof2<;kiEyyw2gl*gyO3YN}M$QWJunlJlzhS;P2! z5+!M#$GOFs0y(Gd4mD@~XdDG;37>P%{^8SoKKNrnu9x4CbGNnT-toC(eC`k*&G-y_ z^p@jj#ix73=?T8oLuWZ3joUH?eEgQo_-HN1@rk2nRsYPbyvtK;UF*8Arffs?cmw$K ztEvBa&1eIERrvG{4E|-!@bTdUt*Bk*b7%0^6u+*y zDPxe2zbPY5{teBh+4OP8=rNx=HXl8^GV(3X9TPWwkF{p(t1`9QGO)63>IFX$#(Ah! z9_DYUDjz32v_36JVC;=g{}|@eFUAYKQhG%n80h;gq^21z_r|w@zSr`(zbi8C`lDFr zS}HJ27ua%vq0x9N1;&0S#hI^aO+I|$^z4mP(0gLG=epMPtDzmyupy)Nk&GBQ=mhUd z7ya6jfze0mp!KE<{72#6Dlp688?YQ|;=?f=%_44RYRLy<7`Cl-Q-(gnb_%R~_PfH! zhfmzj&buOJd*0WYw`8>*Xbr|N?4j0R^^6q5uE@ literal 0 HcmV?d00001 diff --git a/data/shaders/subpasses/gbuffer.vert b/data/shaders/subpasses/gbuffer.vert new file mode 100644 index 00000000..c97ee347 --- /dev/null +++ b/data/shaders/subpasses/gbuffer.vert @@ -0,0 +1,42 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec4 inPos; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec3 inNormal; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec3 outWorldPos; +layout (location = 3) out vec3 outTangent; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = ubo.projection * ubo.view * ubo.model * inPos; + + // Vertex position in world space + outWorldPos = vec3(ubo.model * inPos); + // GL to Vulkan coord space + outWorldPos.y = -outWorldPos.y; + + // Normal in world space + mat3 mNormal = transpose(inverse(mat3(ubo.model))); + outNormal = mNormal * normalize(inNormal); + + // Currently just vertex color + outColor = inColor; +} diff --git a/data/shaders/subpasses/gbuffer.vert.spv b/data/shaders/subpasses/gbuffer.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..22cb7c55aa4fe1a7a0c0501a4ae333012b4b3a13 GIT binary patch literal 2448 zcmZ9M*-{fh6oy+M3W6XaiyH=TM-dSaMRql)?2BHe0!+ndlA$I+y^~kaNAjt>vC9AJ znWH039s2zLS^IQP8XJbrICn~a&2HDltJ5_IJJ;$?`#ra^y)sg(AB>DojN;Mm8oeVv z9qydhoAkd@C{+PxCD$Z3ByS|El1<5$WJl7dzXt6;m7vMBxZFy9VQVp83l0lWp&sOG z9|{LSln?hl2K)8e|2{I6s>OV{P%R!6ia~ySZ1k|O|H);PDcwUxy*H~yv7FxsqIW@5 z555YDjd^rzVXagzg;m8j`o7`O+qLg5u6uuU)?pOV5$@tM-iH}f!h@i!-8jznxfFcS z=5w9bezX@#Z}M{~RaMs8GJZzkQT=@wl@DO?nUt-v7DkmqSvccoD4&EOXU5O8RQ*eN zjv32uDJ+MPRB=sy=A~*(5y$-Hv!nS1)w*-b;+PYwHQroK=ZE7Q%gSNr&v+c48`mNJ zX_3Qx^q3sywK(RJjsp`nFR?hdlX28e$AO7EmRKB|#nCf8z~$64Jq$`%h6iHkG4HcK zg?X!Bi|5^@?Y!Z%omqqFo&3D*G-l3VzLYLyCGUU`7`;c@Y3*4_ui`lem^a)n8}o%x7$qdG|lM9ag6WXZ_Pdt84CC(ks8Gh#Rzxuohi(ipWE$a`wnsC-HZv=lZcHRgv zX6KDypVUil(9Zp^;C_kW{o_lo{3g)vf>`e2f`t3wo@eC4y>v<7&8J&BKHQV}^hk&Q zp*+O&N{EBExJ%NB<326!vUGThyCR)fczo!M_s$)ES8nii34ZY8&T1c!@OJ5YRwo7H zXSHuiKUw>hIC!gbTY9?A9btUnH&utbyeolc8Fx=Q9Cu*caKbVF4MlRF_a(&RkDWew zd+_wh+ZvU?@#g4@nvW##-x55Qa2E4e*kZ^D#>dY7MEc3IPlGUxu!DmNzg#T*^HtvIO49tCEdn +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include "vulkanexamplebase.h" + +#define VERTEX_BUFFER_BIND_ID 0 +#define ENABLE_VALIDATION false + +#define NUM_LIGHTS 32 + +// Vertex layout for this example +std::vector vertexLayout = +{ + vkMeshLoader::VERTEX_LAYOUT_POSITION, + vkMeshLoader::VERTEX_LAYOUT_COLOR, + vkMeshLoader::VERTEX_LAYOUT_NORMAL, +}; + +class VulkanExample : public VulkanExampleBase +{ +public: + struct { + vkMeshLoader::MeshBuffer scene; + } meshes; + + struct { + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + } vertices; + + struct { + glm::mat4 projection; + glm::mat4 model; + glm::mat4 view; + } uboVS, uboOffscreenVS; + + struct Light { + glm::vec4 position; + glm::vec3 color; + float radius; + }; + + struct { + glm::vec4 viewPos; + Light lights[NUM_LIGHTS]; + } uboFragmentLights; + + struct { + vkTools::UniformData vsOffscreen; + vkTools::UniformData fsLights; + } uniformData; + + struct { + VkPipeline offscreen; + } pipelines; + + struct { + VkPipelineLayout offscreen; + } pipelineLayouts; + + struct { + VkDescriptorSet scene; + } descriptorSets; + + VkDescriptorSetLayout descriptorSetLayout; + + // todo + struct { + VkPipelineLayout pipelineLayout; + VkPipeline pipeline; + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSet descriptorSet; + } composition; + + // Framebuffer for offscreen rendering + struct FrameBufferAttachment { + VkImage image; + VkDeviceMemory mem; + VkImageView view; + VkFormat format; + }; + struct Attachments { + FrameBufferAttachment position, normal, albedo; + } attachments; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + enableTextOverlay = false; + title = "Vulkan Example - Subpasses"; + camera.type = Camera::CameraType::firstperson; + camera.movementSpeed = 5.0f; +#ifndef __ANDROID__ + camera.rotationSpeed = 0.25f; +#endif + camera.position = { 9.5f, 4.5f, -5.8f }; + camera.setRotation(glm::vec3(-9.5f, 53.25f, 0.0f)); + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); + } + + ~VulkanExample() + { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + // Frame buffer + + // Color attachments + vkDestroyImageView(device, attachments.position.view, nullptr); + vkDestroyImage(device, attachments.position.image, nullptr); + vkFreeMemory(device, attachments.position.mem, nullptr); + + vkDestroyImageView(device, attachments.normal.view, nullptr); + vkDestroyImage(device, attachments.normal.image, nullptr); + vkFreeMemory(device, attachments.normal.mem, nullptr); + + vkDestroyImageView(device, attachments.albedo.view, nullptr); + vkDestroyImage(device, attachments.albedo.image, nullptr); + vkFreeMemory(device, attachments.albedo.mem, nullptr); + + vkDestroyPipeline(device, pipelines.offscreen, nullptr); + + vkDestroyPipelineLayout(device, pipelineLayouts.offscreen, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + // Meshes + vkMeshLoader::freeMeshBufferResources(device, &meshes.scene); + + // Uniform buffers + vkTools::destroyUniformData(device, &uniformData.vsOffscreen); + vkTools::destroyUniformData(device, &uniformData.fsLights); + } + + // Create a frame buffer attachment + void createAttachment(VkFormat format, VkImageUsageFlags usage, FrameBufferAttachment *attachment) + { + VkImageAspectFlags aspectMask = 0; + VkImageLayout imageLayout; + + attachment->format = format; + + if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + { + aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + assert(aspectMask > 0); + + VkImageCreateInfo image = vkTools::initializers::imageCreateInfo(); + image.imageType = VK_IMAGE_TYPE_2D; + image.format = format; + image.extent.width = width; + image.extent.height = height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = usage | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; // VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT flag is required for input attachments; + image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &attachment->image)); + vkGetImageMemoryRequirements(device, attachment->image, &memReqs); + memAlloc.allocationSize = memReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->mem)); + VK_CHECK_RESULT(vkBindImageMemory(device, attachment->image, attachment->mem, 0)); + + VkImageViewCreateInfo imageView = vkTools::initializers::imageViewCreateInfo(); + imageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageView.format = format; + imageView.subresourceRange = {}; + imageView.subresourceRange.aspectMask = aspectMask; + imageView.subresourceRange.baseMipLevel = 0; + imageView.subresourceRange.levelCount = 1; + imageView.subresourceRange.baseArrayLayer = 0; + imageView.subresourceRange.layerCount = 1; + imageView.image = attachment->image; + VK_CHECK_RESULT(vkCreateImageView(device, &imageView, nullptr, &attachment->view)); + } + + // Create color attachments for the G-Buffer components + void createGBufferAttachments() + { + createAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &attachments.position); // (World space) Positions + createAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &attachments.normal); // (World space) Normals + createAttachment(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &attachments.albedo); // Albedo (color) + } + + // Override framebuffer setup from base class + // Deferred components will be used as frame buffer attachments + void setupFrameBuffer() + { + VkImageView attachments[5]; + + VkFramebufferCreateInfo frameBufferCreateInfo = {}; + frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCreateInfo.pNext = NULL; + frameBufferCreateInfo.renderPass = renderPass; + frameBufferCreateInfo.attachmentCount = 5; + frameBufferCreateInfo.pAttachments = attachments; + frameBufferCreateInfo.width = width; + frameBufferCreateInfo.height = height; + frameBufferCreateInfo.layers = 1; + + // Create frame buffers for every swap chain image + frameBuffers.resize(swapChain.imageCount); + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { + attachments[0] = swapChain.buffers[i].view; + attachments[1] = this->attachments.position.view; + attachments[2] = this->attachments.normal.view; + attachments[3] = this->attachments.albedo.view; + attachments[4] = depthStencil.view; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i])); + } + } + + // Override render pass setup from base class + void setupRenderPass() + { + createGBufferAttachments(); + + std::array attachments{}; + // Color attachment + attachments[0].format = colorformat; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // Deferred attachments + // Position + attachments[1].format = this->attachments.position.format; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Normals + attachments[2].format = this->attachments.normal.format; + attachments[2].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[2].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Albedo + attachments[3].format = this->attachments.albedo.format; + attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[3].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // Depth attachment + attachments[4].format = depthFormat; + attachments[4].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[4].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[4].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[4].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[4].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[4].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[4].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + std::array subpassDescriptions{}; + + // First subpass: Fill G-Buffer components + + VkAttachmentReference colorReferences[4]; + colorReferences[0] = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + colorReferences[1] = { 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + colorReferences[2] = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + colorReferences[3] = { 3, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + VkAttachmentReference depthReference = { 4, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + + subpassDescriptions[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescriptions[0].colorAttachmentCount = 4; + subpassDescriptions[0].pColorAttachments = colorReferences; + subpassDescriptions[0].pDepthStencilAttachment = &depthReference; + + // Second subpass: Final composition (using G-Buffer components) + + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkAttachmentReference inputReferences[3]; + inputReferences[0] = { 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; + inputReferences[1] = { 2, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; + inputReferences[2] = { 3, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; + + subpassDescriptions[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescriptions[1].colorAttachmentCount = 1; + subpassDescriptions[1].pColorAttachments = &colorReference; + subpassDescriptions[1].pDepthStencilAttachment = &depthReference; + subpassDescriptions[1].inputAttachmentCount = 3; + subpassDescriptions[1].pInputAttachments = inputReferences; + + // Subpass dependencies for layout transitions + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + // This dependency transitions the input attachment from color attachment to shader read + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = 1; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[2].srcSubpass = 0; + dependencies[2].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[2].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[2].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[2].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[2].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[2].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = static_cast(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = static_cast(subpassDescriptions.size()); + renderPassInfo.pSubpasses = subpassDescriptions.data(); + renderPassInfo.dependencyCount = static_cast(dependencies.size()); + renderPassInfo.pDependencies = dependencies.data(); + + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass)); + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[5]; + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; + clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; + clearValues[2].color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; + clearValues[3].color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; + clearValues[4].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 5; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + // Set target frame buffer + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + // First sub pass + // Renders the components of the scene to the G-Buffer atttachments + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.offscreen); + + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.offscreen, 0, 1, &descriptorSets.scene, 0, NULL); + vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.scene.vertices.buf, offsets); + vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.scene.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(drawCmdBuffers[i], meshes.scene.indexCount, 1, 0, 0, 0); + + // Second sub pass + // This subpass will use the G-Buffer components that have been filled in the first subpass as input attachment for the final compositing + + vkCmdNextSubpass(drawCmdBuffers[i], VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, composition.pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, composition.pipelineLayout, 0, 1, &composition.descriptorSet, 0, NULL); + vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void loadAssets() + { + loadMesh(getAssetPath() + "models/samplescene.dae", &meshes.scene, vertexLayout, 0.25f); + } + + void setupVertexDescriptions() + { + // Binding description + vertices.bindingDescriptions.resize(1); + vertices.bindingDescriptions[0] = + vkTools::initializers::vertexInputBindingDescription( + VERTEX_BUFFER_BIND_ID, + vkMeshLoader::vertexSize(vertexLayout), + VK_VERTEX_INPUT_RATE_VERTEX); + + // Attribute descriptions + vertices.attributeDescriptions.resize(3); + // Location 0: Position + vertices.attributeDescriptions[0] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 0, + VK_FORMAT_R32G32B32_SFLOAT, + 0); + // Location 1: Color + vertices.attributeDescriptions[1] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 1, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 3); + // Location 2: Normal + vertices.attributeDescriptions[2] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 2, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 6); + + vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); + vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.bindingDescriptions.size()); + vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); + vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); + vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); + } + + void setupDescriptorPool() + { + std::vector poolSizes = + { + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 8), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 9), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 3), + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vkTools::initializers::descriptorPoolCreateInfo( + static_cast(poolSizes.size()), + poolSizes.data(), + 4); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupDescriptorSetLayout() + { + // Deferred shading layout + std::vector setLayoutBindings = + { + // Binding 0 : Vertex shader uniform buffer + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_VERTEX_BIT, + 0) + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vkTools::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + static_cast(setLayoutBindings.size())); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = + vkTools::initializers::pipelineLayoutCreateInfo( + &descriptorSetLayout, + 1); + + // Offscreen (scene) rendering pipeline layout + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayouts.offscreen)); + } + + void setupDescriptorSet() + { + std::vector writeDescriptorSets; + + VkDescriptorSetAllocateInfo allocInfo = + vkTools::initializers::descriptorSetAllocateInfo( + descriptorPool, + &descriptorSetLayout, + 1); + + // Background + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.scene)); + writeDescriptorSets = + { + // Binding 0: Vertex shader uniform buffer + vkTools::initializers::writeDescriptorSet( + descriptorSets.scene, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &uniformData.vsOffscreen.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vkTools::initializers::pipelineInputAssemblyStateCreateInfo( + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + 0, + VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vkTools::initializers::pipelineRasterizationStateCreateInfo( + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_BACK_BIT, + VK_FRONT_FACE_CLOCKWISE, + 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vkTools::initializers::pipelineColorBlendAttachmentState( + 0xf, + VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vkTools::initializers::pipelineColorBlendStateCreateInfo( + 1, + &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vkTools::initializers::pipelineDepthStencilStateCreateInfo( + VK_TRUE, + VK_TRUE, + VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vkTools::initializers::pipelineMultisampleStateCreateInfo( + VK_SAMPLE_COUNT_1_BIT, + 0); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vkTools::initializers::pipelineDynamicStateCreateInfo( + dynamicStateEnables.data(), + static_cast(dynamicStateEnables.size()), + 0); + + // Final fullscreen pass pipeline + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vkTools::initializers::pipelineCreateInfo( + pipelineLayouts.offscreen, + renderPass, + 0); + + pipelineCreateInfo.pVertexInputState = &vertices.inputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + + std::array blendAttachmentStates = { + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE) + }; + + colorBlendState.attachmentCount = static_cast(blendAttachmentStates.size()); + colorBlendState.pAttachments = blendAttachmentStates.data(); + + // Offscreen pipeline + shaderStages[0] = loadShader(getAssetPath() + "shaders/subpasses/gbuffer.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/subpasses/gbuffer.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.offscreen)); + } + + // todo: comment + void prepareCompositionPass() + { + // Descriptor set layout + std::vector setLayoutBindings = + { + // Binding 0: Position input attachment + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0), + // Binding 1: Normal input attachment + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_SHADER_STAGE_FRAGMENT_BIT, + 1), + // Binding 2: Albedo input attachment + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_SHADER_STAGE_FRAGMENT_BIT, + 2), + // Binding 3: Light positions + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 3), + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vkTools::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + static_cast(setLayoutBindings.size())); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &composition.descriptorSetLayout)); + + // Pipeline layout + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = + vkTools::initializers::pipelineLayoutCreateInfo(&composition.descriptorSetLayout, 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &composition.pipelineLayout)); + + // Descriptor sets + VkDescriptorSetAllocateInfo allocInfo = + vkTools::initializers::descriptorSetAllocateInfo(descriptorPool, &composition.descriptorSetLayout, 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &composition.descriptorSet)); + + // Image descriptors for the offscreen color attachments + VkDescriptorImageInfo texDescriptorPosition = + vkTools::initializers::descriptorImageInfo( + nullptr, + attachments.position.view, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkDescriptorImageInfo texDescriptorNormal = + vkTools::initializers::descriptorImageInfo( + nullptr, + attachments.normal.view, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkDescriptorImageInfo texDescriptorAlbedo = + vkTools::initializers::descriptorImageInfo( + nullptr, + attachments.albedo.view, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + std::vector writeDescriptorSets = { + // Binding 0: Position texture target + vkTools::initializers::writeDescriptorSet( + composition.descriptorSet, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + 0, + &texDescriptorPosition), + // Binding 1: Normals texture target + vkTools::initializers::writeDescriptorSet( + composition.descriptorSet, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + 1, + &texDescriptorNormal), + // Binding 2: Albedo texture target + vkTools::initializers::writeDescriptorSet( + composition.descriptorSet, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + 2, + &texDescriptorAlbedo), + // Binding 4: Fragment shader lights + vkTools::initializers::writeDescriptorSet( + composition.descriptorSet, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 3, + &uniformData.fsLights.descriptor), + }; + + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // Pipeline + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vkTools::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vkTools::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vkTools::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + + std::array blendAttachmentStates = { + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE), + }; + + colorBlendState.attachmentCount = static_cast(blendAttachmentStates.size()); + colorBlendState.pAttachments = blendAttachmentStates.data(); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vkTools::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = + vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vkTools::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = + vkTools::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast(dynamicStateEnables.size()), 0); + + std::array shaderStages; + + shaderStages[0] = loadShader(getAssetPath() + "shaders/subpasses/composition.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/subpasses/composition.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + // Use specialization constants to pass number of lights to the shader + VkSpecializationMapEntry specializationEntry{}; + specializationEntry.constantID = 0; + specializationEntry.offset = 0; + specializationEntry.size = sizeof(uint32_t); + + uint32_t specializationData = NUM_LIGHTS; + + VkSpecializationInfo specializationInfo; + specializationInfo.mapEntryCount = 1; + specializationInfo.pMapEntries = &specializationEntry; + specializationInfo.dataSize = sizeof(specializationData); + specializationInfo.pData = &specializationData; + + shaderStages[1].pSpecializationInfo = &specializationInfo; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vkTools::initializers::pipelineCreateInfo(composition.pipelineLayout, renderPass, 0); + + VkPipelineVertexInputStateCreateInfo emptyInputState{}; + emptyInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + pipelineCreateInfo.pVertexInputState = &emptyInputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &composition.pipeline)); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + // Deferred vertex shader + createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + sizeof(uboOffscreenVS), + nullptr, + &uniformData.vsOffscreen.buffer, + &uniformData.vsOffscreen.memory, + &uniformData.vsOffscreen.descriptor); + + // Deferred fragment shader + createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + sizeof(uboFragmentLights), + nullptr, + &uniformData.fsLights.buffer, + &uniformData.fsLights.memory, + &uniformData.fsLights.descriptor); + + // Update + updateUniformBufferDeferredMatrices(); + updateUniformBufferDeferredLights(); + } + + void updateUniformBufferDeferredMatrices() + { + uboOffscreenVS.projection = camera.matrices.perspective; + uboOffscreenVS.view = camera.matrices.view; + uboOffscreenVS.model = glm::mat4(); + + uint8_t *pData; + VK_CHECK_RESULT(vkMapMemory(device, uniformData.vsOffscreen.memory, 0, sizeof(uboOffscreenVS), 0, (void **)&pData)); + memcpy(pData, &uboOffscreenVS, sizeof(uboOffscreenVS)); + vkUnmapMemory(device, uniformData.vsOffscreen.memory); + } + + void initLights() + { + std::vector colors = + { + glm::vec3(1.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f), + glm::vec3(1.0f, 1.0f, 0.0f), + }; + + std::mt19937 rndGen((unsigned)time(NULL)); + std::uniform_real_distribution rndDist(-1.0f, 1.0f); + std::uniform_int_distribution rndCol(0, colors.size()-1); + + for (auto& light : uboFragmentLights.lights) + { + light.position = glm::vec4(rndDist(rndGen) * 6.0f, 0.25f + std::abs(rndDist(rndGen)) * 4.0f, rndDist(rndGen) * 6.0f, 1.0f); + light.color = colors[rndCol(rndGen)]; + light.radius = 1.0f + std::abs(rndDist(rndGen)); + } + } + + // Update fragment shader light position uniform block + void updateUniformBufferDeferredLights() + { + // Current view position + uboFragmentLights.viewPos = glm::vec4(camera.position, 0.0f) * glm::vec4(-1.0f, 1.0f, -1.0f, 1.0f); + + uint8_t *pData; + VK_CHECK_RESULT(vkMapMemory(device, uniformData.fsLights.memory, 0, sizeof(uboFragmentLights), 0, (void **)&pData)); + memcpy(pData, &uboFragmentLights, sizeof(uboFragmentLights)); + vkUnmapMemory(device, uniformData.fsLights.memory); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + + // Command buffer to be sumitted to the queue + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + + // Submit to queue + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + setupVertexDescriptions(); + initLights(); + prepareUniformBuffers(); + setupDescriptorSetLayout(); + preparePipelines(); + setupDescriptorPool(); + setupDescriptorSet(); + prepareCompositionPass(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + } + + virtual void viewChanged() + { + updateUniformBufferDeferredMatrices(); + updateUniformBufferDeferredLights(); + } + + virtual void keyPressed(uint32_t keyCode) + { + switch (keyCode) + { + case KEY_F1: + case GAMEPAD_BUTTON_A: + initLights(); + updateUniformBufferDeferredLights(); + break; + } + } +}; + +VULKAN_EXAMPLE_MAIN() diff --git a/subpasses/subpasses.vcxproj b/subpasses/subpasses.vcxproj new file mode 100644 index 00000000..833e73a8 --- /dev/null +++ b/subpasses/subpasses.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {DE1DEF4B-307E-45C2-90CC-8023327294E2} + Win32Proj + 8.1 + + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + + WIN32;_DEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + true + Windows + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + + + true + Windows + true + true + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/subpasses/subpasses.vcxproj.filters b/subpasses/subpasses.vcxproj.filters new file mode 100644 index 00000000..bf62153e --- /dev/null +++ b/subpasses/subpasses.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {12c674f2-7199-4bf4-b710-4535b9f6d3f8} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/vulkanExamples.sln b/vulkanExamples.sln index d2220fe4..9704fd93 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -115,6 +115,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "deferredmultisampling", "de EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "computecullandlod", "computecullandlod\computecullandlod.vcxproj", "{8418A364-3D1C-4938-A2CC-C1D1433039F2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subpasses", "subpasses\subpasses.vcxproj", "{DE1DEF4B-307E-45C2-90CC-8023327294E2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -285,6 +287,10 @@ Global {8418A364-3D1C-4938-A2CC-C1D1433039F2}.Debug|x64.Build.0 = Debug|x64 {8418A364-3D1C-4938-A2CC-C1D1433039F2}.Release|x64.ActiveCfg = Release|x64 {8418A364-3D1C-4938-A2CC-C1D1433039F2}.Release|x64.Build.0 = Release|x64 + {DE1DEF4B-307E-45C2-90CC-8023327294E2}.Debug|x64.ActiveCfg = Debug|x64 + {DE1DEF4B-307E-45C2-90CC-8023327294E2}.Debug|x64.Build.0 = Debug|x64 + {DE1DEF4B-307E-45C2-90CC-8023327294E2}.Release|x64.ActiveCfg = Release|x64 + {DE1DEF4B-307E-45C2-90CC-8023327294E2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE