diff --git a/examples/displacement/displacement.cpp b/examples/displacement/displacement.cpp index fb96c4bd..6a9e2ac4 100644 --- a/examples/displacement/displacement.cpp +++ b/examples/displacement/displacement.cpp @@ -1,6 +1,8 @@ /* * Vulkan Example - Displacement mapping with tessellation shaders * +* This samples uses tessellation shaders to displace geometry based on a height map +* * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -12,40 +14,32 @@ class VulkanExample : public VulkanExampleBase { -private: - struct { - vks::Texture2D colorHeightMap; - } textures; public: bool splitScreen = true; bool displacement = true; vkglTF::Model plane; + vks::Texture2D colorHeightMap; - struct { - vks::Buffer tessControl, tessEval; - } uniformBuffers; - - struct UBOTessControl { - float tessLevel = 64.0f; - } uboTessControl; - - struct UBOTessEval { + // Uniform data/buffer used by both tessellation shader stages + struct UniformData { glm::mat4 projection; glm::mat4 modelView; glm::vec4 lightPos = glm::vec4(0.0f, -1.0f, 0.0f, 0.0f); float tessAlpha = 1.0f; float tessStrength = 0.1f; - } uboTessEval; + float tessLevel = 64.0f; + } uniformData; + vks::Buffer uniformBuffer; struct Pipelines { - VkPipeline solid; - VkPipeline wireframe = VK_NULL_HANDLE; + VkPipeline solid{ VK_NULL_HANDLE }; + VkPipeline wireframe{ VK_NULL_HANDLE }; } pipelines; - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; - VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; VulkanExample() : VulkanExampleBase() { @@ -58,19 +52,16 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipelines.solid, nullptr); - if (pipelines.wireframe != VK_NULL_HANDLE) { - vkDestroyPipeline(device, pipelines.wireframe, nullptr); - }; - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - uniformBuffers.tessControl.destroy(); - uniformBuffers.tessEval.destroy(); - textures.colorHeightMap.destroy(); + if (device) { + vkDestroyPipeline(device, pipelines.solid, nullptr); + if (pipelines.wireframe != VK_NULL_HANDLE) { + vkDestroyPipeline(device, pipelines.wireframe, nullptr); + }; + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + colorHeightMap.destroy(); + } } // Enable physical device features required for this example @@ -96,7 +87,7 @@ public: { const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; plane.loadFromFile(getAssetPath() + "models/displacement_plane.gltf", vulkanDevice, queue, glTFLoadingFlags); - textures.colorHeightMap.loadFromFile(getAssetPath() + "textures/stonefloor03_color_height_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + colorHeightMap.loadFromFile(getAssetPath() + "textures/stonefloor03_color_height_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); } void buildCommandBuffers() @@ -156,122 +147,56 @@ public: } } - void setupDescriptorPool() + void setupDescriptors() { + // Pool std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); - } - void setupDescriptorSetLayout() - { - std::vector setLayoutBindings = - { - // Binding 0 : Tessellation control shader ubo - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, - 0), - // Binding 1 : Tessellation evaluation shader ubo - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, - 1), - // Binding 2 : Combined color (rgb) and height (alpha) map - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - 2), + // Layout + std::vector setLayoutBindings = { + // Binding 0 : Tessellation shader ubo + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, 0), + // Binding 1 : Combined color (rgb) and height (alpha) map + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 1), }; - - VkDescriptorSetLayoutCreateInfo descriptorLayout = - vks::initializers::descriptorSetLayoutCreateInfo( - setLayoutBindings.data(), - static_cast(setLayoutBindings.size())); - + 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 setupDescriptorSet() - { + // Set VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - std::vector writeDescriptorSets = { - // Binding 0 : Tessellation control shader ubo - vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.tessControl.descriptor), - // Binding 1 : Tessellation evaluation shader ubo - vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &uniformBuffers.tessEval.descriptor), - // Binding 2 : Color and displacement map (alpha channel) - vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &textures.colorHeightMap.descriptor), + // Binding 0 : Tessellation shader ubo + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), + // Binding 1 : Color and displacement map (alpha channel) + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &colorHeightMap.descriptor), }; - vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vks::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, - 0, - VK_FALSE); + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - VkPipelineRasterizationStateCreateInfo rasterizationState = - vks::initializers::pipelineRasterizationStateCreateInfo( - VK_POLYGON_MODE_FILL, - VK_CULL_MODE_BACK_BIT, - VK_FRONT_FACE_COUNTER_CLOCKWISE, - 0); + // Pipelines + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = 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); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_LINE_WIDTH }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + VkPipelineTessellationStateCreateInfo tessellationState = vks::initializers::pipelineTessellationStateCreateInfo(3); - VkPipelineColorBlendAttachmentState blendAttachmentState = - vks::initializers::pipelineColorBlendAttachmentState( - 0xf, - VK_FALSE); - - VkPipelineColorBlendStateCreateInfo colorBlendState = - vks::initializers::pipelineColorBlendStateCreateInfo( - 1, - &blendAttachmentState); - - VkPipelineDepthStencilStateCreateInfo depthStencilState = - vks::initializers::pipelineDepthStencilStateCreateInfo( - VK_TRUE, - VK_TRUE, - VK_COMPARE_OP_LESS_OR_EQUAL); - - VkPipelineViewportStateCreateInfo viewportState = - vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); - - VkPipelineMultisampleStateCreateInfo multisampleState = - vks::initializers::pipelineMultisampleStateCreateInfo( - VK_SAMPLE_COUNT_1_BIT, - 0); - - std::vector dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_LINE_WIDTH - }; - VkPipelineDynamicStateCreateInfo dynamicState = - vks::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - static_cast(dynamicStateEnables.size()), - 0); - - VkPipelineTessellationStateCreateInfo tessellationState = - vks::initializers::pipelineTessellationStateCreateInfo(3); - - // Tessellation pipeline // Load shaders std::array shaderStages; shaderStages[0] = loadShader(getShadersPath() + "displacement/base.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); @@ -292,6 +217,7 @@ public: pipelineCI.pStages = shaderStages.data(); pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV }); + // Tessellation pipelines // Solid pipeline VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.solid)); if (deviceFeatures.fillModeNonSolid) { @@ -306,88 +232,54 @@ public: void prepareUniformBuffers() { // Tessellation evaluation shader uniform buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffers.tessEval, - sizeof(uboTessEval))); - - // Tessellation control shader uniform buffer - VK_CHECK_RESULT(vulkanDevice->createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &uniformBuffers.tessControl, - sizeof(uboTessControl))); - + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData))); // Map persistent - VK_CHECK_RESULT(uniformBuffers.tessControl.map()); - VK_CHECK_RESULT(uniformBuffers.tessEval.map()); - - updateUniformBuffers(); + VK_CHECK_RESULT(uniformBuffer.map()); } void updateUniformBuffers() { - uboTessEval.projection = camera.matrices.perspective; - uboTessEval.modelView = camera.matrices.view; - uboTessEval.lightPos.y = -0.5f - uboTessEval.tessStrength; - memcpy(uniformBuffers.tessEval.mapped, &uboTessEval, sizeof(uboTessEval)); - + uniformData.projection = camera.matrices.perspective; + uniformData.modelView = camera.matrices.view; + uniformData.lightPos.y = -0.5f - uniformData.tessStrength; // Tessellation control - float savedLevel = uboTessControl.tessLevel; - if (!displacement) - { - uboTessControl.tessLevel = 1.0f; + float savedLevel = uniformData.tessLevel; + // If displacement is unchecked, we simply set the tessellation level to 1.0f, which disables tessellation + if (!displacement) { + uniformData.tessLevel = 1.0f; } - - memcpy(uniformBuffers.tessControl.mapped, &uboTessControl, sizeof(uboTessControl)); - - if (!displacement) - { - uboTessControl.tessLevel = savedLevel; + memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData)); + if (!displacement) { + uniformData.tessLevel = savedLevel; } } - 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(); - } - void prepare() { VulkanExampleBase::prepare(); loadAssets(); prepareUniformBuffers(); - setupDescriptorSetLayout(); + setupDescriptors(); preparePipelines(); - setupDescriptorPool(); - setupDescriptorSet(); 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 (camera.updated) { - updateUniformBuffers(); - } - } - - virtual void viewChanged() - { updateUniformBuffers(); + draw(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) @@ -396,10 +288,10 @@ public: if (overlay->checkBox("Tessellation displacement", &displacement)) { updateUniformBuffers(); } - if (overlay->inputFloat("Strength", &uboTessEval.tessStrength, 0.025f, 3)) { + if (overlay->inputFloat("Strength", &uniformData.tessStrength, 0.025f, 3)) { updateUniformBuffers(); } - if (overlay->inputFloat("Level", &uboTessControl.tessLevel, 0.5f, 2)) { + if (overlay->inputFloat("Level", &uniformData.tessLevel, 0.5f, 2)) { updateUniformBuffers(); } if (deviceFeatures.fillModeNonSolid) { diff --git a/shaders/glsl/displacement/base.frag b/shaders/glsl/displacement/base.frag index 4f18178e..1d3cb1ba 100644 --- a/shaders/glsl/displacement/base.frag +++ b/shaders/glsl/displacement/base.frag @@ -1,6 +1,6 @@ #version 450 -layout (binding = 2) uniform sampler2D colorMap; +layout (binding = 1) uniform sampler2D colorMap; layout (location = 0) in vec3 inNormal; layout (location = 1) in vec2 inUV; diff --git a/shaders/glsl/displacement/base.frag.spv b/shaders/glsl/displacement/base.frag.spv index 3d35236f..c6e57c8c 100644 Binary files a/shaders/glsl/displacement/base.frag.spv and b/shaders/glsl/displacement/base.frag.spv differ diff --git a/shaders/glsl/displacement/displacement.tesc b/shaders/glsl/displacement/displacement.tesc index 31bbe507..abfc21e0 100644 --- a/shaders/glsl/displacement/displacement.tesc +++ b/shaders/glsl/displacement/displacement.tesc @@ -2,6 +2,11 @@ layout (binding = 0) uniform UBO { + mat4 projection; + mat4 modelview; + vec4 lightPos; + float tessAlpha; + float tessStrength; float tessLevel; } ubo; diff --git a/shaders/glsl/displacement/displacement.tesc.spv b/shaders/glsl/displacement/displacement.tesc.spv index a85c805d..2a7c0f55 100644 Binary files a/shaders/glsl/displacement/displacement.tesc.spv and b/shaders/glsl/displacement/displacement.tesc.spv differ diff --git a/shaders/glsl/displacement/displacement.tese b/shaders/glsl/displacement/displacement.tese index afe12d1a..e9e750ac 100644 --- a/shaders/glsl/displacement/displacement.tese +++ b/shaders/glsl/displacement/displacement.tese @@ -1,15 +1,16 @@ #version 450 -layout (binding = 1) uniform UBO +layout (binding = 0) uniform UBO { mat4 projection; mat4 modelview; vec4 lightPos; float tessAlpha; float tessStrength; + float tessLevel; } ubo; -layout (binding = 2) uniform sampler2D displacementMap; +layout (binding = 1) uniform sampler2D displacementMap; layout(triangles, equal_spacing, cw) in; diff --git a/shaders/glsl/displacement/displacement.tese.spv b/shaders/glsl/displacement/displacement.tese.spv index 4a697f15..859a936d 100644 Binary files a/shaders/glsl/displacement/displacement.tese.spv and b/shaders/glsl/displacement/displacement.tese.spv differ diff --git a/shaders/hlsl/displacement/base.frag b/shaders/hlsl/displacement/base.frag index c62eec65..78bda883 100644 --- a/shaders/hlsl/displacement/base.frag +++ b/shaders/hlsl/displacement/base.frag @@ -1,7 +1,8 @@ // Copyright 2020 Google LLC +// Copyright 2023 Sascha Willems -Texture2D textureColorMap : register(t2); -SamplerState samplerColorMap : register(s2); +Texture2D textureColorMap : register(t1); +SamplerState samplerColorMap : register(s1); struct DSOutput { diff --git a/shaders/hlsl/displacement/base.frag.spv b/shaders/hlsl/displacement/base.frag.spv index 918714cd..7c63170b 100644 Binary files a/shaders/hlsl/displacement/base.frag.spv and b/shaders/hlsl/displacement/base.frag.spv differ diff --git a/shaders/hlsl/displacement/displacement.tesc b/shaders/hlsl/displacement/displacement.tesc index 470fe7c2..ab2ac6bb 100644 --- a/shaders/hlsl/displacement/displacement.tesc +++ b/shaders/hlsl/displacement/displacement.tesc @@ -1,7 +1,13 @@ // Copyright 2020 Google LLC +// Copyright 2023 Sascha Willems struct UBO { + float4x4 projection; + float4x4 model; + float4 lightPos; + float tessAlpha; + float tessStrength; float tessLevel; }; diff --git a/shaders/hlsl/displacement/displacement.tesc.spv b/shaders/hlsl/displacement/displacement.tesc.spv index 49f15d8c..17a9bdfc 100644 Binary files a/shaders/hlsl/displacement/displacement.tesc.spv and b/shaders/hlsl/displacement/displacement.tesc.spv differ diff --git a/shaders/hlsl/displacement/displacement.tese b/shaders/hlsl/displacement/displacement.tese index 0732c345..86aeb19e 100644 --- a/shaders/hlsl/displacement/displacement.tese +++ b/shaders/hlsl/displacement/displacement.tese @@ -1,4 +1,5 @@ // Copyright 2020 Google LLC +// Copyright 2023 Sascha Willems struct UBO { @@ -7,12 +8,13 @@ struct UBO float4 lightPos; float tessAlpha; float tessStrength; + float tessLevel; }; -cbuffer ubo : register(b1) { UBO ubo; } +cbuffer ubo : register(b0) { UBO ubo; } -Texture2D textureDisplacementMap : register(t2); -SamplerState samplerDisplacementMap : register(s2); +Texture2D textureDisplacementMap : register(t1); +SamplerState samplerDisplacementMap : register(s1); struct HSOutput { diff --git a/shaders/hlsl/displacement/displacement.tese.spv b/shaders/hlsl/displacement/displacement.tese.spv index 8e360fae..d028af64 100644 Binary files a/shaders/hlsl/displacement/displacement.tese.spv and b/shaders/hlsl/displacement/displacement.tese.spv differ