diff --git a/data/shaders/shadowmappingcascade/debugshadowmap.frag b/data/shaders/shadowmappingcascade/debugshadowmap.frag index 70aaf94a..37bcad80 100644 --- a/data/shaders/shadowmappingcascade/debugshadowmap.frag +++ b/data/shaders/shadowmappingcascade/debugshadowmap.frag @@ -3,15 +3,12 @@ layout (binding = 1) uniform sampler2DArray shadowMap; layout (location = 0) in vec2 inUV; +layout (location = 1) flat in uint inCascadeIndex; layout (location = 0) out vec4 outFragColor; -layout(push_constant) uniform PushConsts { - uint cascadeIndex; -} pushConsts; - void main() { - float depth = texture(shadowMap, vec3(inUV, float(pushConsts.cascadeIndex))).r; + float depth = texture(shadowMap, vec3(inUV, float(inCascadeIndex))).r; outFragColor = vec4(vec3((depth)), 1.0); } \ No newline at end of file diff --git a/data/shaders/shadowmappingcascade/debugshadowmap.frag.spv b/data/shaders/shadowmappingcascade/debugshadowmap.frag.spv index 04713235..89357ed7 100644 Binary files a/data/shaders/shadowmappingcascade/debugshadowmap.frag.spv and b/data/shaders/shadowmappingcascade/debugshadowmap.frag.spv differ diff --git a/data/shaders/shadowmappingcascade/debugshadowmap.vert b/data/shaders/shadowmappingcascade/debugshadowmap.vert index 780bdf00..8db331f2 100644 --- a/data/shaders/shadowmappingcascade/debugshadowmap.vert +++ b/data/shaders/shadowmappingcascade/debugshadowmap.vert @@ -1,14 +1,20 @@ #version 450 +layout(push_constant) uniform PushConsts { + vec4 position; + uint cascadeIndex; +} pushConsts; + layout (location = 0) out vec2 outUV; +layout (location = 1) out uint outCascadeIndex; out gl_PerVertex { vec4 gl_Position; }; - void main() { outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + outCascadeIndex = pushConsts.cascadeIndex; gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f); } diff --git a/data/shaders/shadowmappingcascade/debugshadowmap.vert.spv b/data/shaders/shadowmappingcascade/debugshadowmap.vert.spv index f9cc1345..83219d1e 100644 Binary files a/data/shaders/shadowmappingcascade/debugshadowmap.vert.spv and b/data/shaders/shadowmappingcascade/debugshadowmap.vert.spv differ diff --git a/data/shaders/shadowmappingcascade/depthpass.frag b/data/shaders/shadowmappingcascade/depthpass.frag index 9941acf4..2e239363 100644 --- a/data/shaders/shadowmappingcascade/depthpass.frag +++ b/data/shaders/shadowmappingcascade/depthpass.frag @@ -1,5 +1,13 @@ #version 450 +layout (set = 1, binding = 0) uniform sampler2D colorMap; + +layout (location = 0) in vec2 inUV; + void main() { + float alpha = texture(colorMap, inUV).a; + if (alpha < 0.5) { + discard; + } } \ No newline at end of file diff --git a/data/shaders/shadowmappingcascade/depthpass.frag.spv b/data/shaders/shadowmappingcascade/depthpass.frag.spv index b128ff56..803c2dca 100644 Binary files a/data/shaders/shadowmappingcascade/depthpass.frag.spv and b/data/shaders/shadowmappingcascade/depthpass.frag.spv differ diff --git a/data/shaders/shadowmappingcascade/depthpass.vert b/data/shaders/shadowmappingcascade/depthpass.vert index 9bf8daf2..0abfbda0 100644 --- a/data/shaders/shadowmappingcascade/depthpass.vert +++ b/data/shaders/shadowmappingcascade/depthpass.vert @@ -1,11 +1,13 @@ #version 450 layout (location = 0) in vec3 inPos; +layout (location = 1) in vec2 inUV; // todo: pass via specialization constant #define SHADOW_MAP_CASCADE_COUNT 4 layout(push_constant) uniform PushConsts { + vec4 position; uint cascadeIndex; } pushConsts; @@ -13,11 +15,15 @@ layout (binding = 0) uniform UBO { mat4[SHADOW_MAP_CASCADE_COUNT] cascadeViewProjMat; } ubo; +layout (location = 0) out vec2 outUV; + out gl_PerVertex { vec4 gl_Position; }; void main() { - gl_Position = ubo.cascadeViewProjMat[pushConsts.cascadeIndex] * vec4(inPos, 1.0); + outUV = inUV; + vec3 pos = inPos + pushConsts.position.xyz; + gl_Position = ubo.cascadeViewProjMat[pushConsts.cascadeIndex] * vec4(pos, 1.0); } \ No newline at end of file diff --git a/data/shaders/shadowmappingcascade/depthpass.vert.spv b/data/shaders/shadowmappingcascade/depthpass.vert.spv index c9a61d1a..60475d85 100644 Binary files a/data/shaders/shadowmappingcascade/depthpass.vert.spv and b/data/shaders/shadowmappingcascade/depthpass.vert.spv differ diff --git a/data/shaders/shadowmappingcascade/scene.frag b/data/shaders/shadowmappingcascade/scene.frag index 9ba3e9c5..470b5da6 100644 --- a/data/shaders/shadowmappingcascade/scene.frag +++ b/data/shaders/shadowmappingcascade/scene.frag @@ -2,20 +2,22 @@ #define SHADOW_MAP_CASCADE_COUNT 4 -layout (binding = 1) uniform sampler2DArray shadowMap; +layout (set = 0, binding = 1) uniform sampler2DArray shadowMap; +layout (set = 1, binding = 0) uniform sampler2D colorMap; layout (location = 0) in vec3 inNormal; layout (location = 1) in vec3 inColor; layout (location = 2) in vec3 inViewPos; layout (location = 3) in vec3 inPos; +layout (location = 4) in vec2 inUV; layout (constant_id = 0) const int enablePCF = 0; layout (location = 0) out vec4 outFragColor; -#define ambient 0.1 +#define ambient 0.3 -layout (binding = 2) uniform UBO { +layout (set = 0, binding = 2) uniform UBO { vec4 cascadeSplits; mat4 cascadeViewProjMat[SHADOW_MAP_CASCADE_COUNT]; mat4 inverseViewMat; @@ -69,6 +71,11 @@ float filterPCF(vec4 sc, uint cascadeIndex) void main() { + vec4 color = texture(colorMap, inUV); + if (color.a < 0.5) { + discard; + } + // Get cascade index for the current fragment's view position uint cascadeIndex = 0; for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; ++i) { @@ -91,10 +98,11 @@ void main() vec3 N = normalize(inNormal); vec3 L = normalize(-ubo.lightDir); vec3 H = normalize(L + inViewPos); - float diffuse = max(dot(N, L), 0.0); + float diffuse = max(dot(N, L), ambient); vec3 lightColor = vec3(1.0); - outFragColor.rgb = max(lightColor * (diffuse * inColor), vec3(0.0)); + outFragColor.rgb = max(lightColor * (diffuse * color.rgb), vec3(0.0)); outFragColor.rgb *= shadow; + outFragColor.a = color.a; // Color cascades (if enabled) if (ubo.colorCascades == 1) { diff --git a/data/shaders/shadowmappingcascade/scene.frag.spv b/data/shaders/shadowmappingcascade/scene.frag.spv index 20bef5b8..35124f21 100644 Binary files a/data/shaders/shadowmappingcascade/scene.frag.spv and b/data/shaders/shadowmappingcascade/scene.frag.spv differ diff --git a/data/shaders/shadowmappingcascade/scene.vert b/data/shaders/shadowmappingcascade/scene.vert index a75dfd89..4ef0b3a9 100644 --- a/data/shaders/shadowmappingcascade/scene.vert +++ b/data/shaders/shadowmappingcascade/scene.vert @@ -15,6 +15,12 @@ layout (location = 0) out vec3 outNormal; layout (location = 1) out vec3 outColor; layout (location = 2) out vec3 outViewPos; layout (location = 3) out vec3 outPos; +layout (location = 4) out vec2 outUV; + +layout(push_constant) uniform PushConsts { + vec4 position; + uint cascadeIndex; +} pushConsts; out gl_PerVertex { vec4 gl_Position; @@ -24,8 +30,10 @@ void main() { outColor = inColor; outNormal = inNormal; - outPos = inPos; - outViewPos = (ubo.view * vec4(inPos.xyz, 1.0)).xyz; - gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPos.xyz, 1.0); + outUV = inUV; + vec3 pos = inPos + pushConsts.position.xyz; + outPos = pos; + outViewPos = (ubo.view * vec4(pos.xyz, 1.0)).xyz; + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(pos.xyz, 1.0); } diff --git a/data/shaders/shadowmappingcascade/scene.vert.spv b/data/shaders/shadowmappingcascade/scene.vert.spv index f7395d5d..612e8fea 100644 Binary files a/data/shaders/shadowmappingcascade/scene.vert.spv and b/data/shaders/shadowmappingcascade/scene.vert.spv differ diff --git a/examples/shadowmappingcascade/shadowmappingcascade.cpp b/examples/shadowmappingcascade/shadowmappingcascade.cpp index da236042..be539ca0 100644 --- a/examples/shadowmappingcascade/shadowmappingcascade.cpp +++ b/examples/shadowmappingcascade/shadowmappingcascade.cpp @@ -21,6 +21,7 @@ #include #include "vulkanexamplebase.h" #include "VulkanBuffer.hpp" +#include "VulkanTexture.hpp" #include "VulkanModel.hpp" #define ENABLE_VALIDATION false @@ -28,7 +29,7 @@ #if defined(__ANDROID__) #define SHADOWMAP_DIM 1024 #else -#define SHADOWMAP_DIM 2048 +#define SHADOWMAP_DIM 4096 #endif #define SHADOW_MAP_CASCADE_COUNT 4 @@ -41,7 +42,7 @@ public: bool colorCascades = false; bool filterPCF = false; - float cascadeSplitLambda = 1.0f; + float cascadeSplitLambda = 0.95f; float zNear = 0.5f; float zFar = 48.0f; @@ -56,23 +57,27 @@ public: vks::VERTEX_COMPONENT_NORMAL, }); - std::vector scenes; - std::vector sceneNames; - int32_t sceneIndex = 0; + std::vector models; - struct { + struct Material { + vks::Texture2D texture; + VkDescriptorSet descriptorSet; + }; + std::vector materials; + + struct uniformBuffers { vks::Buffer VS; vks::Buffer FS; } uniformBuffers; - struct { + struct UBOVS { glm::mat4 projection; glm::mat4 view; glm::mat4 model; glm::vec3 lightDir; } uboVS; - struct { + struct UBOFS { float cascadeSplits[4]; glm::mat4 cascadeViewProjMat[4]; glm::mat4 inverseViewMat; @@ -81,15 +86,24 @@ public: int32_t colorCascades; } uboFS; - struct { + VkPipelineLayout pipelineLayout; + struct Pipelines { VkPipeline debugShadowMap; VkPipeline sceneShadow; VkPipeline sceneShadowPCF; } pipelines; - VkPipelineLayout pipelineLayout; + struct DescriptorSetLayouts { + VkDescriptorSetLayout base; + VkDescriptorSetLayout material; + } descriptorSetLayouts; VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + + // For simplicity all pipelines use the same push constant block layout + struct PushConstBlock { + glm::vec4 position; + uint32_t cascadeIndex; + }; // Resources of the depth map generation pass struct DepthPass { @@ -139,15 +153,18 @@ public: VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) { title = "Cascaded shadow mapping"; - timerSpeed *= 0.05f; + timerSpeed *= 0.025f; camera.type = Camera::CameraType::firstperson; camera.movementSpeed = 2.5f; camera.setPerspective(45.0f, (float)width / (float)height, zNear, zFar); - camera.setPosition(glm::vec3(2.0f, 0.375f, -1.25f)); - camera.setRotation(glm::vec3(-19.0f, 42.0f, 0.0f)); + camera.setPosition(glm::vec3(0.0f, 0.62f, -2.4f)); + camera.setRotation(glm::vec3(0.0f, -13.0f, 0.0f)); + + camera.setPosition(glm::vec3(-0.12f, 1.14f, -2.25f)); + camera.setRotation(glm::vec3(-17.0f, 7.0f, 0.0f)); + settings.overlay = true; - timer = 0.317028880f; - paused = true; + timer = 0.2f; } ~VulkanExample() @@ -167,10 +184,14 @@ public: vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyPipelineLayout(device, depthPass.pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.base, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); - for (auto scene : scenes) { - scene.destroy(); + for (auto model : models) { + model.destroy(); + } + for (auto material : materials) { + material.texture.destroy(); } depthPass.uniformBuffer.destroy(); @@ -183,6 +204,7 @@ public: virtual void getEnabledFeatures() { + enabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy; // Depth clamp to avoid near plane clipping enabledFeatures.depthClamp = deviceFeatures.depthClamp; } @@ -194,7 +216,7 @@ public: vks::tools::getSupportedDepthFormat(physicalDevice, &depthFormat); depthPass.commandBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); - // Create a semaphore used to synchronize offscreen rendering and usage + // Create a semaphore used to synchronize depth map generation and use VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo(); VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &depthPass.semaphore)); @@ -328,6 +350,48 @@ public: VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &depth.sampler)); } + void renderScene(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VkDescriptorSet descriptorSet, uint32_t cascadeIndex = 0) { + const VkDeviceSize offsets[1] = { 0 }; + PushConstBlock pushConstBlock = { glm::vec4(0.0f), cascadeIndex }; + + std::array sets; + sets[0] = descriptorSet; + + // Floor + sets[1] = materials[0].descriptorSet; + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, sets.data(), 0, NULL); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &models[0].vertices.buffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, models[0].indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(commandBuffer, models[0].indexCount, 1, 0, 0, 0); + + // Trees + const std::vector positions = { + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(1.25f, 0.25f, 1.25f), + glm::vec3(-1.25f, -0.2f, 1.25f), + glm::vec3(1.25f, 0.1f, -1.25f), + glm::vec3(-1.25f, -0.25f, -1.25f), + }; + + for (auto position : positions) { + pushConstBlock.position = glm::vec4(position, 0.0f); + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); + + sets[1] = materials[1].descriptorSet; + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, sets.data(), 0, NULL); + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &models[1].vertices.buffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, models[1].indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(commandBuffer, models[1].indexCount, 1, 0, 0, 0); + + sets[1] = materials[2].descriptorSet; + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, sets.data(), 0, NULL); + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &models[2].vertices.buffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, models[2].indices.buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(commandBuffer, models[2].indexCount, 1, 0, 0, 0); + } + } + void buildOffscreenCommandBuffer() { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); @@ -358,12 +422,9 @@ public: renderPassBeginInfo.framebuffer = cascades[i].frameBuffer; vkCmdBeginRenderPass(depthPass.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(depthPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, depthPass.pipeline); - vkCmdPushConstants(depthPass.commandBuffer, depthPass.pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(uint32_t), &i); VkDeviceSize offsets[1] = { 0 }; vkCmdBindDescriptorSets(depthPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, depthPass.pipelineLayout, 0, 1, &cascades[i].descriptorSet, 0, NULL); - vkCmdBindVertexBuffers(depthPass.commandBuffer, 0, 1, &scenes[sceneIndex].vertices.buffer, offsets); - vkCmdBindIndexBuffer(depthPass.commandBuffer, scenes[sceneIndex].indices.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(depthPass.commandBuffer, scenes[sceneIndex].indexCount, 1, 0, 0, 0); + renderScene(depthPass.commandBuffer, depthPass.pipelineLayout, cascades[i].descriptorSet, i); vkCmdEndRenderPass(depthPass.commandBuffer); } @@ -407,16 +468,15 @@ public: if (displayDepthMap) { vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.debugShadowMap); - vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(uint32_t), &displayDepthMapCascadeIndex); + PushConstBlock pushConstBlock = {}; + pushConstBlock.cascadeIndex = displayDepthMapCascadeIndex; + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock); vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); } // Render shadowed scene - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, (filterPCF) ? pipelines.sceneShadowPCF : pipelines.sceneShadow); - vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &scenes[sceneIndex].vertices.buffer, offsets); - vkCmdBindIndexBuffer(drawCmdBuffers[i], scenes[sceneIndex].indices.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(drawCmdBuffers[i], scenes[sceneIndex].indexCount, 1, 0, 0, 0); + renderScene(drawCmdBuffers[i], pipelineLayout, descriptorSet); vkCmdEndRenderPass(drawCmdBuffers[i]); @@ -426,80 +486,65 @@ public: void loadAssets() { - scenes.resize(2); - scenes[0].loadFromFile(getAssetPath() + "models/trees.dae", vertexLayout, 1.0f, vulkanDevice, queue); - scenes[1].loadFromFile(getAssetPath() + "models/samplescene.dae", vertexLayout, 0.25f, vulkanDevice, queue); - sceneNames = { "Trees", "Teapots and pillars" }; + materials.resize(3); + materials[0].texture.loadFromFile(getAssetPath() + "textures/gridlines.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + materials[1].texture.loadFromFile(getAssetPath() + "textures/oak_bark.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + materials[2].texture.loadFromFile(getAssetPath() + "textures/oak_leafs.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + models.resize(3); + models[0].loadFromFile(getAssetPath() + "models/terrain_simple.dae", vertexLayout, 1.0f, vulkanDevice, queue); + models[1].loadFromFile(getAssetPath() + "models/oak_trunk.dae", vertexLayout, 2.0f, vulkanDevice, queue); + models[2].loadFromFile(getAssetPath() + "models/oak_leafs.dae", vertexLayout, 2.0f, vulkanDevice, queue); } - void setupDescriptorPool() + void setupLayoutsAndDescriptors() { + /* + Descriptor pool + */ std::vector poolSizes = { - vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 12), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 32), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 32) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = - vks::initializers::descriptorPoolCreateInfo(static_cast(poolSizes.size()), poolSizes.data(), 3 + SHADOW_MAP_CASCADE_COUNT); + vks::initializers::descriptorPoolCreateInfo(static_cast(poolSizes.size()), poolSizes.data(), 4 + SHADOW_MAP_CASCADE_COUNT); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupLayoutsAndDescriptors() - { /* - Layouts + Descriptor set layouts */ + // Shared matrices and samplers std::vector setLayoutBindings = { vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), - vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 3), }; - VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayouts.base)); - // Shared pipeline layout - { - // Pass cascade index as push constant - VkPushConstantRange pushConstantRange = - vks::initializers::pushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(uint32_t), 0); - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - pipelineLayoutCreateInfo.pushConstantRangeCount = 1; - pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - } - - // Offscreen pipeline layout - { - // Pass cascade matrix as push constant - VkPushConstantRange pushConstantRange = - vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::mat4), 0); - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); - pipelineLayoutCreateInfo.pushConstantRangeCount = 1; - pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &depthPass.pipelineLayout)); - } + // Material texture + setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), + }; + descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayouts.material)); /* - Dscriptor sets + Descriptor sets */ std::vector writeDescriptorSets; - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); - - // Scene rendering / debug display - VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - VkDescriptorImageInfo depthMapDescriptor = vks::initializers::descriptorImageInfo(depth.sampler, depth.view, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + VkDescriptorSetAllocateInfo allocInfo = + vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.base, 1); + + // Scene rendering / debug display + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); writeDescriptorSets = { vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.VS.descriptor), vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &depthMapDescriptor), @@ -507,7 +552,7 @@ public: }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); - // Per-cascade descriptor set + // Per-cascade descriptor sets // Each descriptor set represents a single layer of the array texture for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) { VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &cascades[i].descriptorSet)); @@ -518,6 +563,38 @@ public: }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } + + // Per-material descriptor sets + allocInfo.pSetLayouts = &descriptorSetLayouts.material; + for (auto& material : materials) { + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &material.descriptorSet)); + VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(material.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &material.texture.descriptor); + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); + } + + /* + Pipeline layouts + */ + + // Shared pipeline layout (scene and depth map debug display) + { + VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0); + std::array setLayouts = { descriptorSetLayouts.base, descriptorSetLayouts.material }; + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast(setLayouts.size())); + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + } + + // Depth pass pipeline layout + { + VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0); + std::array setLayouts = { descriptorSetLayouts.base, descriptorSetLayouts.material }; + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast(setLayouts.size())); + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &depthPass.pipelineLayout)); + } } void preparePipelines() @@ -597,7 +674,7 @@ public: /* Shadow mapped scene rendering */ - rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizationState.cullMode = VK_CULL_MODE_NONE; shaderStages[0] = loadShader(getAssetPath() + "shaders/shadowmappingcascade/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/shadowmappingcascade/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); // Use specialization constants to select between horizontal and vertical blur @@ -605,9 +682,7 @@ public: VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t)); VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(uint32_t), &enablePCF); shaderStages[1].pSpecializationInfo = &specializationInfo; - // No filtering VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.sceneShadow)); - // PCF filtering enablePCF = 1; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.sceneShadowPCF)); @@ -816,7 +891,6 @@ public: updateCascades(); prepareShadowMaps(); prepareUniformBuffers(); - setupDescriptorPool(); setupLayoutsAndDescriptors(); preparePipelines(); buildCommandBuffers(); @@ -845,10 +919,6 @@ public: virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) { if (overlay->header("Settings")) { - if (overlay->comboBox("Scenes", &sceneIndex, sceneNames)) { - buildCommandBuffers(); - buildOffscreenCommandBuffer(); - } if (overlay->sliderFloat("Split lambda", &cascadeSplitLambda, 0.1f, 1.0f)) { updateCascades(); updateUniformBuffers();