diff --git a/examples/shadowmapping/shadowmapping.cpp b/examples/shadowmapping/shadowmapping.cpp index 40a4d88e..59d9474e 100644 --- a/examples/shadowmapping/shadowmapping.cpp +++ b/examples/shadowmapping/shadowmapping.cpp @@ -9,17 +9,6 @@ #include "vulkanexamplebase.h" #include "VulkanglTFModel.h" -// 16 bits of depth is enough for such a small scene -#define DEPTH_FORMAT VK_FORMAT_D16_UNORM - -// Shadowmap properties -#if defined(__ANDROID__) -#define SHADOWMAP_DIM 1024 -#else -#define SHADOWMAP_DIM 2048 -#endif -#define DEFAULT_SHADOWMAP_FILTER VK_FILTER_LINEAR - class VulkanExample : public VulkanExampleBase { public: @@ -44,12 +33,7 @@ public: std::vector sceneNames; int32_t sceneIndex = 0; - struct { - vks::Buffer scene; - vks::Buffer offscreen; - } uniformBuffers; - - struct { + struct UniformDataScene { glm::mat4 projection; glm::mat4 view; glm::mat4 model; @@ -58,27 +42,32 @@ public: // Used for depth map visualization float zNear; float zFar; - } uboVSscene; + } uniformDataScene; - struct { + struct UniformDataOffscreen { glm::mat4 depthMVP; - } uboOffscreenVS; + } uniformDataOffscreen; struct { - VkPipeline offscreen; - VkPipeline sceneShadow; - VkPipeline sceneShadowPCF; - VkPipeline debug; + vks::Buffer scene; + vks::Buffer offscreen; + } uniformBuffers; + + struct { + VkPipeline offscreen{ VK_NULL_HANDLE }; + VkPipeline sceneShadow{ VK_NULL_HANDLE }; + // Pipeline with percentage close filtering (PCF) of the shadow map + VkPipeline sceneShadowPCF{ VK_NULL_HANDLE }; + VkPipeline debug{ VK_NULL_HANDLE }; } pipelines; - VkPipelineLayout pipelineLayout; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; struct { - VkDescriptorSet offscreen; - VkDescriptorSet scene; - VkDescriptorSet debug; + VkDescriptorSet offscreen{ VK_NULL_HANDLE }; + VkDescriptorSet scene{ VK_NULL_HANDLE }; + VkDescriptorSet debug{ VK_NULL_HANDLE }; } descriptorSets; - - VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; // Framebuffer for offscreen rendering struct FrameBufferAttachment { @@ -93,47 +82,56 @@ public: VkRenderPass renderPass; VkSampler depthSampler; VkDescriptorImageInfo descriptor; - } offscreenPass; + } offscreenPass{}; + + // 16 bits of depth is enough for such a small scene + const VkFormat offscreenDepthFormat{ VK_FORMAT_D16_UNORM }; + // Shadow map dimension +#if defined(__ANDROID__) + // Use a smaller size on Android for performance reasons + const uint32_t shadowMapize{ 1024 }; +#else + const uint32_t shadowMapize{ 2048 }; +#endif VulkanExample() : VulkanExampleBase() { title = "Projected shadow mapping"; camera.type = Camera::CameraType::lookat; - camera.setPosition(glm::vec3(0.0f, -0.0f, -20.0f)); - camera.setRotation(glm::vec3(-15.0f, -390.0f, 0.0f)); + camera.setPosition(glm::vec3(0.0f, 0.0f, -12.5f)); + camera.setRotation(glm::vec3(-25.0f, -390.0f, 0.0f)); camera.setPerspective(60.0f, (float)width / (float)height, 1.0f, 256.0f); timerSpeed *= 0.5f; } ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class + if (device) { + // Frame buffer + vkDestroySampler(device, offscreenPass.depthSampler, nullptr); - // Frame buffer - vkDestroySampler(device, offscreenPass.depthSampler, nullptr); + // Depth attachment + vkDestroyImageView(device, offscreenPass.depth.view, nullptr); + vkDestroyImage(device, offscreenPass.depth.image, nullptr); + vkFreeMemory(device, offscreenPass.depth.mem, nullptr); - // Depth attachment - vkDestroyImageView(device, offscreenPass.depth.view, nullptr); - vkDestroyImage(device, offscreenPass.depth.image, nullptr); - vkFreeMemory(device, offscreenPass.depth.mem, nullptr); + vkDestroyFramebuffer(device, offscreenPass.frameBuffer, nullptr); - vkDestroyFramebuffer(device, offscreenPass.frameBuffer, nullptr); + vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr); - vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr); + vkDestroyPipeline(device, pipelines.debug, nullptr); + vkDestroyPipeline(device, pipelines.offscreen, nullptr); + vkDestroyPipeline(device, pipelines.sceneShadow, nullptr); + vkDestroyPipeline(device, pipelines.sceneShadowPCF, nullptr); - vkDestroyPipeline(device, pipelines.debug, nullptr); - vkDestroyPipeline(device, pipelines.offscreen, nullptr); - vkDestroyPipeline(device, pipelines.sceneShadow, nullptr); - vkDestroyPipeline(device, pipelines.sceneShadowPCF, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - // Uniform buffers - uniformBuffers.offscreen.destroy(); - uniformBuffers.scene.destroy(); + // Uniform buffers + uniformBuffers.offscreen.destroy(); + uniformBuffers.scene.destroy(); + } } // Set up a separate render pass for the offscreen frame buffer @@ -141,7 +139,7 @@ public: void prepareOffscreenRenderpass() { VkAttachmentDescription attachmentDescription{}; - attachmentDescription.format = DEPTH_FORMAT; + attachmentDescription.format = offscreenDepthFormat; attachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clear depth at beginning of the render pass attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // We will read from depth, so it's important to store the depth attachment results @@ -193,8 +191,8 @@ public: // The depth attachment of this framebuffer will then be used to sample from in the fragment shader of the shadowing pass void prepareOffscreenFramebuffer() { - offscreenPass.width = SHADOWMAP_DIM; - offscreenPass.height = SHADOWMAP_DIM; + offscreenPass.width = shadowMapize; + offscreenPass.height = shadowMapize; // For shadow mapping we only need a depth attachment VkImageCreateInfo image = vks::initializers::imageCreateInfo(); @@ -206,7 +204,7 @@ public: image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; - image.format = DEPTH_FORMAT; // Depth stencil attachment + image.format = offscreenDepthFormat; // Depth stencil attachment image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // We will sample directly from the depth attachment for the shadow mapping VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.depth.image)); @@ -220,7 +218,7 @@ public: VkImageViewCreateInfo depthStencilView = vks::initializers::imageViewCreateInfo(); depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; - depthStencilView.format = DEPTH_FORMAT; + depthStencilView.format = offscreenDepthFormat; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; depthStencilView.subresourceRange.baseMipLevel = 0; @@ -232,9 +230,7 @@ public: // Create sampler to sample from to depth attachment // Used to sample in the fragment shader for shadowed rendering - VkFilter shadowmap_filter = vks::tools::formatIsFilterable(physicalDevice, DEPTH_FORMAT, VK_IMAGE_TILING_OPTIMAL) ? - DEFAULT_SHADOWMAP_FILTER : - VK_FILTER_NEAREST; + VkFilter shadowmap_filter = vks::tools::formatIsFilterable(physicalDevice, offscreenDepthFormat, VK_IMAGE_TILING_OPTIMAL) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; VkSamplerCreateInfo sampler = vks::initializers::samplerCreateInfo(); sampler.magFilter = shadowmap_filter; sampler.minFilter = shadowmap_filter; @@ -370,19 +366,17 @@ public: sceneNames = {"Vulkan scene", "Teapots and pillars" }; } - void setupDescriptorPool() + void setupDescriptors() { + // Pool std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 3); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { - // Shared pipeline layout for all pipelines used in this sample + // Layout std::vector setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0), @@ -391,20 +385,16 @@ public: }; VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - void setupDescriptorSets() - { + // Sets std::vector writeDescriptorSets; // Image descriptor for the shadow map attachment VkDescriptorImageInfo shadowMapDescriptor = - vks::initializers::descriptorImageInfo( - offscreenPass.depthSampler, - offscreenPass.depth.view, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + vks::initializers::descriptorImageInfo( + offscreenPass.depthSampler, + offscreenPass.depth.view, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); // Debug display VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); @@ -413,7 +403,7 @@ public: // Binding 0 : Parameters uniform buffer vks::initializers::writeDescriptorSet(descriptorSets.debug, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.scene.descriptor), // Binding 1 : Fragment shader texture sampler - vks::initializers::writeDescriptorSet(descriptorSets.debug, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &shadowMapDescriptor) + vks::initializers::writeDescriptorSet(descriptorSets.debug, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &shadowMapDescriptor) }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); @@ -431,13 +421,18 @@ public: // Binding 0 : Vertex shader uniform buffer vks::initializers::writeDescriptorSet(descriptorSets.scene, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.scene.descriptor), // Binding 1 : Fragment shader shadow sampler - vks::initializers::writeDescriptorSet(descriptorSets.scene, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &shadowMapDescriptor) + vks::initializers::writeDescriptorSet(descriptorSets.scene, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &shadowMapDescriptor) }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + // Pipelines VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); @@ -507,19 +502,9 @@ public: void prepareUniformBuffers() { // Offscreen vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffers.offscreen, - sizeof(uboOffscreenVS))); - + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffers.offscreen, sizeof(UniformDataOffscreen))); // Scene vertex shader uniform buffer block - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffers.scene, - sizeof(uboVSscene))); - + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffers.scene, sizeof(UniformDataScene))); // Map persistent VK_CHECK_RESULT(uniformBuffers.offscreen.map()); VK_CHECK_RESULT(uniformBuffers.scene.map()); @@ -539,14 +524,14 @@ public: void updateUniformBuffers() { - uboVSscene.projection = camera.matrices.perspective; - uboVSscene.view = camera.matrices.view; - uboVSscene.model = glm::mat4(1.0f); - uboVSscene.lightPos = glm::vec4(lightPos, 1.0f); - uboVSscene.depthBiasMVP = uboOffscreenVS.depthMVP; - uboVSscene.zNear = zNear; - uboVSscene.zFar = zFar; - memcpy(uniformBuffers.scene.mapped, &uboVSscene, sizeof(uboVSscene)); + uniformDataScene.projection = camera.matrices.perspective; + uniformDataScene.view = camera.matrices.view; + uniformDataScene.model = glm::mat4(1.0f); + uniformDataScene.lightPos = glm::vec4(lightPos, 1.0f); + uniformDataScene.depthBiasMVP = uniformDataOffscreen.depthMVP; + uniformDataScene.zNear = zNear; + uniformDataScene.zFar = zFar; + memcpy(uniformBuffers.scene.mapped, &uniformDataScene, sizeof(uniformDataScene)); } void updateUniformBufferOffscreen() @@ -556,23 +541,9 @@ public: glm::mat4 depthViewMatrix = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0, 1, 0)); glm::mat4 depthModelMatrix = glm::mat4(1.0f); - uboOffscreenVS.depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix; + uniformDataOffscreen.depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix; - memcpy(uniformBuffers.offscreen.mapped, &uboOffscreenVS, sizeof(uboOffscreenVS)); - } - - void draw() - { - VulkanExampleBase::prepareFrame(); - - // Command buffer to be submitted 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(); + memcpy(uniformBuffers.offscreen.mapped, &uniformDataOffscreen, sizeof(uniformDataOffscreen)); } void prepare() @@ -581,25 +552,31 @@ public: loadAssets(); prepareOffscreenFramebuffer(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSets(); buildCommandBuffers(); prepared = true; } + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + virtual void render() { if (!prepared) return; - draw(); - if (!paused || camera.updated) - { + if (!paused || camera.updated) { updateLight(); updateUniformBufferOffscreen(); updateUniformBuffers(); } + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)