diff --git a/data/shaders/ssao/blur.frag b/data/shaders/ssao/blur.frag new file mode 100644 index 00000000..33830d00 --- /dev/null +++ b/data/shaders/ssao/blur.frag @@ -0,0 +1,28 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 0) uniform sampler2D samplerSSAO; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out float outFragColor; + +const int blurSize = 4; + +void main() +{ + const int blurRange = 2; + vec2 texelSize = 1.0 / vec2(textureSize(samplerSSAO, 0)); + float result = 0.0; + for (int x = -blurRange; x < blurRange; x++) + { + for (int y = -blurRange; y < blurRange; y++) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + result += texture(samplerSSAO, inUV + offset).r; + } + } + outFragColor = result / (blurRange * blurRange * blurRange * blurRange); +} \ No newline at end of file diff --git a/data/shaders/ssao/blur.frag.spv b/data/shaders/ssao/blur.frag.spv new file mode 100644 index 00000000..5b0439dc Binary files /dev/null and b/data/shaders/ssao/blur.frag.spv differ diff --git a/data/shaders/ssao/composition.frag b/data/shaders/ssao/composition.frag new file mode 100644 index 00000000..96d9e041 --- /dev/null +++ b/data/shaders/ssao/composition.frag @@ -0,0 +1,51 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 0) uniform sampler2D samplerposition; +layout (binding = 1) uniform sampler2D samplerNormal; +layout (binding = 2) uniform sampler2D samplerAlbedo; +layout (binding = 3) uniform sampler2D samplerSSAO; +layout (binding = 4) uniform sampler2D samplerSSAOBlur; +layout (binding = 5) uniform UBO +{ + mat4 _dummy; + uint ssao; + uint ssaoOnly; + uint ssaoBlur; +} uboParams; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + vec3 fragPos = texture(samplerposition, inUV).rgb; + vec3 normal = texture(samplerNormal, inUV).rgb; + vec4 albedo = texture(samplerAlbedo, inUV); + + float ssao = (uboParams.ssaoBlur == 1) ? texture(samplerSSAOBlur, inUV).r : texture(samplerSSAO, inUV).r; + + outFragColor = vec4(vec3(0.0), 1.0); + + if (uboParams.ssaoOnly == 1) + { + outFragColor.rgb = ssao.rrr; + } + else + { + if (uboParams.ssao == 1) + { + outFragColor.rgb = ssao.rrr; + + if (uboParams.ssaoOnly != 1) + outFragColor.rgb *= albedo.rgb; + } + else + { + outFragColor.rgb = albedo.rgb; + } + } +} \ No newline at end of file diff --git a/data/shaders/ssao/composition.frag.spv b/data/shaders/ssao/composition.frag.spv new file mode 100644 index 00000000..b421dc90 Binary files /dev/null and b/data/shaders/ssao/composition.frag.spv differ diff --git a/data/shaders/ssao/fullscreen.vert b/data/shaders/ssao/fullscreen.vert new file mode 100644 index 00000000..8a38fdeb --- /dev/null +++ b/data/shaders/ssao/fullscreen.vert @@ -0,0 +1,17 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) out vec2 outUV; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f); +} diff --git a/data/shaders/ssao/fullscreen.vert.spv b/data/shaders/ssao/fullscreen.vert.spv new file mode 100644 index 00000000..77f02b8b Binary files /dev/null and b/data/shaders/ssao/fullscreen.vert.spv differ diff --git a/data/shaders/ssao/gbuffer.frag b/data/shaders/ssao/gbuffer.frag new file mode 100644 index 00000000..13ed06f0 --- /dev/null +++ b/data/shaders/ssao/gbuffer.frag @@ -0,0 +1,29 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec2 inUV; +layout (location = 2) in vec3 inColor; +layout (location = 3) in vec3 inPos; + +layout (location = 0) out vec4 outPosition; +layout (location = 1) out vec4 outNormal; +layout (location = 2) out vec4 outAlbedo; + +const float NEAR_PLANE = 0.1f; //todo: specialization const +const float FAR_PLANE = 50.0f; //todo: specialization const + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} + +void main() +{ + outPosition = vec4(inPos, linearDepth(gl_FragCoord.z)); + outNormal = vec4(normalize(inNormal) * 0.5 + 0.5, 1.0); + outAlbedo = vec4(inColor * 2.0, 1.0); +} \ No newline at end of file diff --git a/data/shaders/ssao/gbuffer.frag.spv b/data/shaders/ssao/gbuffer.frag.spv new file mode 100644 index 00000000..869f86cb Binary files /dev/null and b/data/shaders/ssao/gbuffer.frag.spv differ diff --git a/data/shaders/ssao/gbuffer.vert b/data/shaders/ssao/gbuffer.vert new file mode 100644 index 00000000..2b56a145 --- /dev/null +++ b/data/shaders/ssao/gbuffer.vert @@ -0,0 +1,43 @@ +#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 vec2 inUV; +layout (location = 2) in vec3 inColor; +layout (location = 3) in vec3 inNormal; +layout (location = 4) in vec3 inTangent; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec2 outUV; +layout (location = 2) out vec3 outColor; +layout (location = 3) out vec3 outPos; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = ubo.projection * ubo.view * ubo.model * inPos; + + outUV = inUV; + + // Vertex position in view space + outPos = vec3(ubo.view * ubo.model * inPos); + + // Normal in view space + mat3 normalMatrix = transpose(inverse(mat3(ubo.view * ubo.model))); + outNormal = normalMatrix * inNormal; + + outColor = inColor; +} diff --git a/data/shaders/ssao/gbuffer.vert.spv b/data/shaders/ssao/gbuffer.vert.spv new file mode 100644 index 00000000..3f8b5835 Binary files /dev/null and b/data/shaders/ssao/gbuffer.vert.spv differ diff --git a/data/shaders/ssao/ssao.frag b/data/shaders/ssao/ssao.frag new file mode 100644 index 00000000..fcb3e2b8 --- /dev/null +++ b/data/shaders/ssao/ssao.frag @@ -0,0 +1,67 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 0) uniform sampler2D samplerPositionDepth; +layout (binding = 1) uniform sampler2D samplerNormal; +layout (binding = 2) uniform sampler2D ssaoNoise; + +layout (binding = 3) uniform UBOSSAOKernel +{ + vec4 samples[64]; // todo: specialization const +} uboSSAOKernel; + +layout (binding = 4) uniform UBO +{ + mat4 projection; +} ubo; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out float outFragColor; + +// todo: specialization const +const int kernelSize = 64; +const float radius = 1.0; + +void main() +{ + // Get G-Buffer values + vec3 fragPos = texture(samplerPositionDepth, inUV).rgb; + vec3 normal = normalize(texture(samplerNormal, inUV).rgb) * 2.0 - 1.0f; + + // Get a random vector using a noise lookup + ivec2 texDim = textureSize(samplerPositionDepth, 0); + const vec2 noiseUV = vec2(float(texDim.x)/4.0f, float(texDim.y)/4.0f) * inUV; + vec3 randomVec = texture(ssaoNoise, noiseUV).xyz * 2.0 - 1.0; + + // Create TBN matrix + vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + vec3 bitangent = cross(tangent, normal); + mat3 TBN = mat3(tangent, bitangent, normal); + + // Calculate occlusion value + float occlusion = 0.0f; + for(int i = 0; i < kernelSize; i++) + { + vec3 samplePos = TBN * uboSSAOKernel.samples[i].xyz; + samplePos = fragPos + samplePos * radius; + + // project + vec4 offset = vec4(samplePos, 1.0f); + offset = ubo.projection * offset; + offset.xyz /= offset.w; + offset.xyz = offset.xyz * 0.5f + 0.5f; + + float sampleDepth = -texture(samplerPositionDepth, offset.xy).w; + + // Range check + float rangeCheck = smoothstep(0.0f, 1.0f, radius / abs(fragPos.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z ? 1.0f : 0.0f) * rangeCheck; + } + occlusion = 1.0 - (occlusion / float(kernelSize)); + + outFragColor = occlusion; +} + diff --git a/data/shaders/ssao/ssao.frag.spv b/data/shaders/ssao/ssao.frag.spv new file mode 100644 index 00000000..a2f67cb5 Binary files /dev/null and b/data/shaders/ssao/ssao.frag.spv differ diff --git a/ssao/ssao.cpp b/ssao/ssao.cpp new file mode 100644 index 00000000..586559ee --- /dev/null +++ b/ssao/ssao.cpp @@ -0,0 +1,1148 @@ +/* +* Vulkan Example - Screen space ambient occlusion example +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#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 SSAO_KERNEL_SIZE 64 +#define SSAO_NOISE_DIM 4 + +// Vertex layout for this example +std::vector vertexLayout = +{ + vkMeshLoader::VERTEX_LAYOUT_POSITION, + vkMeshLoader::VERTEX_LAYOUT_UV, + vkMeshLoader::VERTEX_LAYOUT_COLOR, + vkMeshLoader::VERTEX_LAYOUT_NORMAL, +}; + +class VulkanExample : public VulkanExampleBase +{ +public: + struct { + vkTools::VulkanTexture ssaoNoise; + } textures; + + struct { + vkMeshLoader::MeshBuffer scene; + } meshes; + + struct { + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + } vertices; + + struct UBOSceneMatrices { + glm::mat4 projection; + glm::mat4 model; + glm::mat4 view; + } uboSceneMatrices; + + struct UBOSSAOParams { + glm::mat4 projection; + uint32_t ssao = true; + uint32_t ssaoOnly = false; + uint32_t ssaoBlur = true; + } uboSSAOParams; + + struct { + VkPipeline offscreen; + VkPipeline composition; + VkPipeline ssao; + VkPipeline ssaoBlur; + } pipelines; + + struct { + VkPipelineLayout gBuffer; + VkPipelineLayout ssao; + VkPipelineLayout ssaoBlur; + VkPipelineLayout composition; + } pipelineLayouts; + + struct { + const uint32_t count = 5; + VkDescriptorSet model; + VkDescriptorSet floor; + VkDescriptorSet ssao; + VkDescriptorSet ssaoBlur; + VkDescriptorSet composition; + } descriptorSets; + + struct { + VkDescriptorSetLayout gBuffer; + VkDescriptorSetLayout ssao; + VkDescriptorSetLayout ssaoBlur; + VkDescriptorSetLayout composition; + } descriptorSetLayouts; + + struct { + vk::Buffer sceneMatrices; + vk::Buffer ssaoKernel; + vk::Buffer ssaoParams; + } uniformBuffers; + + // Framebuffer for offscreen rendering + struct FrameBufferAttachment { + VkImage image; + VkDeviceMemory mem; + VkImageView view; + VkFormat format; + void destroy(VkDevice device) + { + vkDestroyImage(device, image, nullptr); + vkDestroyImageView(device, view, nullptr); + vkFreeMemory(device, mem, nullptr); + } + }; + struct FrameBuffer { + int32_t width, height; + VkFramebuffer frameBuffer; + VkRenderPass renderPass; + void setSize(int32_t w, int32_t h) + { + this->width = w; + this->height = h; + } + void destroy(VkDevice device) + { + vkDestroyFramebuffer(device, frameBuffer, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + } + }; + + struct { + struct Offscreen : public FrameBuffer { + FrameBufferAttachment position, normal, albedo, depth; + } offscreen; + struct SSAO : public FrameBuffer { + FrameBufferAttachment color; + } ssao, ssaoBlur; + } frameBuffers; + + // One sampler for the frame buffer color attachments + VkSampler colorSampler; + + VkCommandBuffer offScreenCmdBuffer = VK_NULL_HANDLE; + + // Semaphore used to synchronize between offscreen and final scene rendering + VkSemaphore offscreenSemaphore = VK_NULL_HANDLE; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + zoom = -8.0f; + rotation = { 0.0f, 0.0f, 0.0f }; + enableTextOverlay = true; + title = "Vulkan Example - Screen space ambient occlusion"; + camera.type = Camera::CameraType::firstperson; + camera.movementSpeed = 5.0f; +#ifndef __ANDROID__ + camera.rotationSpeed = 0.25f; +#endif + camera.position = { 1.9f, 2.6f, -9.25f }; + camera.setRotation(glm::vec3(-15.5f, 10.75f, 0.0f)); + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 50.0f); + } + + ~VulkanExample() + { + vkDestroySampler(device, colorSampler, nullptr); + + // Attachments + frameBuffers.offscreen.position.destroy(device); + frameBuffers.offscreen.normal.destroy(device); + frameBuffers.offscreen.albedo.destroy(device); + frameBuffers.offscreen.depth.destroy(device); + frameBuffers.ssao.color.destroy(device); + frameBuffers.ssaoBlur.color.destroy(device); + + // Framebuffers + frameBuffers.offscreen.destroy(device); + frameBuffers.ssao.destroy(device); + frameBuffers.ssaoBlur.destroy(device); + + vkDestroyPipeline(device, pipelines.offscreen, nullptr); + vkDestroyPipeline(device, pipelines.composition, nullptr); + vkDestroyPipeline(device, pipelines.ssao, nullptr); + vkDestroyPipeline(device, pipelines.ssaoBlur, nullptr); + + vkDestroyPipelineLayout(device, pipelineLayouts.gBuffer, nullptr); + vkDestroyPipelineLayout(device, pipelineLayouts.ssao, nullptr); + vkDestroyPipelineLayout(device, pipelineLayouts.ssaoBlur, nullptr); + vkDestroyPipelineLayout(device, pipelineLayouts.composition, nullptr); + + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.gBuffer, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssao, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssaoBlur, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.composition, nullptr); + + // Meshes + vkMeshLoader::freeMeshBufferResources(device, &meshes.scene); + + // Uniform buffers + uniformBuffers.sceneMatrices.destroy(); + uniformBuffers.ssaoKernel.destroy(); + uniformBuffers.ssaoParams.destroy(); + + // Misc + vkFreeCommandBuffers(device, cmdPool, 1, &offScreenCmdBuffer); + vkDestroySemaphore(device, offscreenSemaphore, nullptr); + textureLoader->destroyTexture(textures.ssaoNoise); + } + + // Create a frame buffer attachment + void createAttachment( + VkFormat format, + VkImageUsageFlagBits usage, + FrameBufferAttachment *attachment, + VkCommandBuffer layoutCmd) + { + 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 = frameBuffers.offscreen.width; + image.extent.height = frameBuffers.offscreen.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_SAMPLED_BIT; + + 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)); + } + + void prepareOffscreenFramebuffers() + { + // Attachments + VkCommandBuffer layoutCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + frameBuffers.offscreen.setSize(width, height); + frameBuffers.ssao.setSize(width, height); + frameBuffers.ssaoBlur.setSize(width, height); + + // Find a suitable depth format + VkFormat attDepthFormat; + VkBool32 validDepthFormat = vkTools::getSupportedDepthFormat(physicalDevice, &attDepthFormat); + assert(validDepthFormat); + + // G-Buffer + createAttachment(VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &frameBuffers.offscreen.position, layoutCmd); // Position + Depth + createAttachment(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &frameBuffers.offscreen.normal, layoutCmd); // Normals + createAttachment(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &frameBuffers.offscreen.albedo, layoutCmd); // Albedo (color) + createAttachment(attDepthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, &frameBuffers.offscreen.depth, layoutCmd); // Depth + + // SSAO + createAttachment(VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &frameBuffers.ssao.color, layoutCmd); // Color + + // SSAO blur + createAttachment(VK_FORMAT_R8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, &frameBuffers.ssaoBlur.color, layoutCmd); // Color + + VulkanExampleBase::flushCommandBuffer(layoutCmd, queue, true); + + // Render passes + + // G-Buffer creation + { + std::array attachmentDescs = {}; + + // Init attachment properties + for (uint32_t i = 0; i < static_cast(attachmentDescs.size()); i++) + { + attachmentDescs[i].samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescs[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescs[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescs[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescs[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescs[i].finalLayout = (i == 3) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + // Formats + attachmentDescs[0].format = frameBuffers.offscreen.position.format; + attachmentDescs[1].format = frameBuffers.offscreen.normal.format; + attachmentDescs[2].format = frameBuffers.offscreen.albedo.format; + attachmentDescs[3].format = frameBuffers.offscreen.depth.format; + + std::vector colorReferences; + colorReferences.push_back({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); + colorReferences.push_back({ 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); + colorReferences.push_back({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); + + VkAttachmentReference depthReference = {}; + depthReference.attachment = 3; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.pColorAttachments = colorReferences.data(); + subpass.colorAttachmentCount = static_cast(colorReferences.size()); + subpass.pDepthStencilAttachment = &depthReference; + + // Use subpass dependencies for attachment 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; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pAttachments = attachmentDescs.data(); + renderPassInfo.attachmentCount = static_cast(attachmentDescs.size()); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 2; + renderPassInfo.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &frameBuffers.offscreen.renderPass)); + + std::array attachments; + attachments[0] = frameBuffers.offscreen.position.view; + attachments[1] = frameBuffers.offscreen.normal.view; + attachments[2] = frameBuffers.offscreen.albedo.view; + attachments[3] = frameBuffers.offscreen.depth.view; + + VkFramebufferCreateInfo fbufCreateInfo = vkTools::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = frameBuffers.offscreen.renderPass; + fbufCreateInfo.pAttachments = attachments.data(); + fbufCreateInfo.attachmentCount = static_cast(attachments.size()); + fbufCreateInfo.width = frameBuffers.offscreen.width; + fbufCreateInfo.height = frameBuffers.offscreen.height; + fbufCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &frameBuffers.offscreen.frameBuffer)); + } + + // SSAO + { + VkAttachmentDescription attachmentDescription{}; + attachmentDescription.format = frameBuffers.ssao.color.format; + attachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachmentDescription.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.pColorAttachments = &colorReference; + subpass.colorAttachmentCount = 1; + + 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; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pAttachments = &attachmentDescription; + renderPassInfo.attachmentCount = 1; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 2; + renderPassInfo.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &frameBuffers.ssao.renderPass)); + + VkFramebufferCreateInfo fbufCreateInfo = vkTools::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = frameBuffers.ssao.renderPass; + fbufCreateInfo.pAttachments = &frameBuffers.ssao.color.view; + fbufCreateInfo.attachmentCount = 1; + fbufCreateInfo.width = frameBuffers.ssao.width; + fbufCreateInfo.height = frameBuffers.ssao.height; + fbufCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &frameBuffers.ssao.frameBuffer)); + } + + // SSAO Blur + { + VkAttachmentDescription attachmentDescription{}; + attachmentDescription.format = frameBuffers.ssao.color.format; + attachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachmentDescription.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.pColorAttachments = &colorReference; + subpass.colorAttachmentCount = 1; + + 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; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pAttachments = &attachmentDescription; + renderPassInfo.attachmentCount = 1; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 2; + renderPassInfo.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &frameBuffers.ssaoBlur.renderPass)); + + VkFramebufferCreateInfo fbufCreateInfo = vkTools::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = frameBuffers.ssaoBlur.renderPass; + fbufCreateInfo.pAttachments = &frameBuffers.ssaoBlur.color.view; + fbufCreateInfo.attachmentCount = 1; + fbufCreateInfo.width = frameBuffers.ssaoBlur.width; + fbufCreateInfo.height = frameBuffers.ssaoBlur.height; + fbufCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &frameBuffers.ssaoBlur.frameBuffer)); + } + + // Shared sampler used for all color attachments + // todo: separate for nearest (G-Buffer) an linear (ssao, blur, etc.) + VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); + sampler.magFilter = VK_FILTER_NEAREST; + sampler.minFilter = VK_FILTER_NEAREST; + sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.addressModeV = sampler.addressModeU; + sampler.addressModeW = sampler.addressModeU; + sampler.mipLodBias = 0.0f; + sampler.maxAnisotropy = 0; + sampler.minLod = 0.0f; + sampler.maxLod = 1.0f; + sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &colorSampler)); + } + + // Build command buffer for rendering the scene to the offscreen frame buffer attachments + void buildDeferredCommandBuffer() + { + VkDeviceSize offsets[1] = { 0 }; + + if (offScreenCmdBuffer == VK_NULL_HANDLE) + { + offScreenCmdBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); + } + + // Create a semaphore used to synchronize offscreen rendering and usage + VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); + VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenSemaphore)); + + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + // Clear values for all attachments written in the fragment sahder + std::vector clearValues(4); + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[2].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[3].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = frameBuffers.offscreen.renderPass; + renderPassBeginInfo.framebuffer = frameBuffers.offscreen.frameBuffer; + renderPassBeginInfo.renderArea.extent.width = frameBuffers.offscreen.width; + renderPassBeginInfo.renderArea.extent.height = frameBuffers.offscreen.height; + renderPassBeginInfo.clearValueCount = static_cast(clearValues.size()); + renderPassBeginInfo.pClearValues = clearValues.data(); + + VK_CHECK_RESULT(vkBeginCommandBuffer(offScreenCmdBuffer, &cmdBufInfo)); + + // First pass: Fill G-Buffer components (positions+depth, normals, albedo) using MRT + // ------------------------------------------------------------------------------------------------------- + + vkCmdBeginRenderPass(offScreenCmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkTools::initializers::viewport((float)frameBuffers.offscreen.width, (float)frameBuffers.offscreen.height, 0.0f, 1.0f); + vkCmdSetViewport(offScreenCmdBuffer, 0, 1, &viewport); + + VkRect2D scissor = vkTools::initializers::rect2D(frameBuffers.offscreen.width, frameBuffers.offscreen.height, 0, 0); + vkCmdSetScissor(offScreenCmdBuffer, 0, 1, &scissor); + + vkCmdBindPipeline(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.offscreen); + + vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.gBuffer, 0, 1, &descriptorSets.floor, 0, NULL); + vkCmdBindVertexBuffers(offScreenCmdBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.scene.vertices.buf, offsets); + vkCmdBindIndexBuffer(offScreenCmdBuffer, meshes.scene.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(offScreenCmdBuffer, meshes.scene.indexCount, 1, 0, 0, 0); + + vkCmdEndRenderPass(offScreenCmdBuffer); + + // Second pass: SSAO generation + // ------------------------------------------------------------------------------------------------------- + + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[1].depthStencil = { 1.0f, 0 }; + + renderPassBeginInfo.framebuffer = frameBuffers.ssao.frameBuffer; + renderPassBeginInfo.renderPass = frameBuffers.ssao.renderPass; + renderPassBeginInfo.renderArea.extent.width = frameBuffers.ssao.width; + renderPassBeginInfo.renderArea.extent.height = frameBuffers.ssao.height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues.data(); + + vkCmdBeginRenderPass(offScreenCmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + viewport = vkTools::initializers::viewport((float)frameBuffers.ssao.width, (float)frameBuffers.ssao.height, 0.0f, 1.0f); + vkCmdSetViewport(offScreenCmdBuffer, 0, 1, &viewport); + scissor = vkTools::initializers::rect2D(frameBuffers.ssao.width, frameBuffers.ssao.height, 0, 0); + vkCmdSetScissor(offScreenCmdBuffer, 0, 1, &scissor); + + vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.ssao, 0, 1, &descriptorSets.ssao, 0, NULL); + vkCmdBindPipeline(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.ssao); + vkCmdDraw(offScreenCmdBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(offScreenCmdBuffer); + + // Third pass: SSAO blur + // ------------------------------------------------------------------------------------------------------- + + renderPassBeginInfo.framebuffer = frameBuffers.ssaoBlur.frameBuffer; + renderPassBeginInfo.renderPass = frameBuffers.ssaoBlur.renderPass; + renderPassBeginInfo.renderArea.extent.width = frameBuffers.ssaoBlur.width; + renderPassBeginInfo.renderArea.extent.height = frameBuffers.ssaoBlur.height; + + vkCmdBeginRenderPass(offScreenCmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + viewport = vkTools::initializers::viewport((float)frameBuffers.ssaoBlur.width, (float)frameBuffers.ssaoBlur.height, 0.0f, 1.0f); + vkCmdSetViewport(offScreenCmdBuffer, 0, 1, &viewport); + scissor = vkTools::initializers::rect2D(frameBuffers.ssaoBlur.width, frameBuffers.ssaoBlur.height, 0, 0); + vkCmdSetScissor(offScreenCmdBuffer, 0, 1, &scissor); + + vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.ssaoBlur, 0, 1, &descriptorSets.ssaoBlur, 0, NULL); + vkCmdBindPipeline(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.ssaoBlur); + vkCmdDraw(offScreenCmdBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(offScreenCmdBuffer); + + VK_CHECK_RESULT(vkEndCommandBuffer(offScreenCmdBuffer)); + } + + void loadAssets() + { + vkMeshLoader::MeshCreateInfo meshCreateInfo; + meshCreateInfo.scale = glm::vec3(1.0f); + meshCreateInfo.uvscale = glm::vec2(1.0f); + meshCreateInfo.center = glm::vec3(0.0f, 0.0f, 0.0f); + loadMesh(getAssetPath() + "models/treasure_smooth.dae", &meshes.scene, vertexLayout, &meshCreateInfo); + } + + void reBuildCommandBuffers() + { + if (!checkCommandBuffers()) + { + destroyCommandBuffers(); + createCommandBuffers(); + } + buildCommandBuffers(); + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; + clearValues[1].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 = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + // Set target frame buffer + renderPassBeginInfo.framebuffer = VulkanExampleBase::frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + 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 }; + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.composition, 0, 1, &descriptorSets.composition, 0, NULL); + + // Final composition pass + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.composition); + vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + 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(4); + // Location 0: Position + vertices.attributeDescriptions[0] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 0, + VK_FORMAT_R32G32B32_SFLOAT, + 0); + // Location 1: Texture coordinates + vertices.attributeDescriptions[1] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 1, + VK_FORMAT_R32G32_SFLOAT, + sizeof(float) * 3); + // Location 2: Color + vertices.attributeDescriptions[2] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 2, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 5); + // Location 3: Normal + vertices.attributeDescriptions[3] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 3, + VK_FORMAT_R32G32B32_SFLOAT, + sizeof(float) * 8); + + 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, 10), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 12) + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vkTools::initializers::descriptorPoolCreateInfo( + static_cast(poolSizes.size()), + poolSizes.data(), + descriptorSets.count); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupLayoutsAndDescriptors() + { + std::vector setLayoutBindings; + VkDescriptorSetLayoutCreateInfo setLayoutCreateInfo; + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vkTools::initializers::pipelineLayoutCreateInfo(); + VkDescriptorSetAllocateInfo descriptorAllocInfo = vkTools::initializers::descriptorSetAllocateInfo(descriptorPool, nullptr, 1); + std::vector writeDescriptorSets; + std::vector imageDescriptors; + + // G-Buffer creation (offscreen scene rendering) + setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // VS UBO + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), // FS Color + }; + setLayoutCreateInfo = vkTools::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast(setLayoutBindings.size())); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &setLayoutCreateInfo, nullptr, &descriptorSetLayouts.gBuffer)); + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayouts.gBuffer; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.gBuffer)); + descriptorAllocInfo.pSetLayouts = &descriptorSetLayouts.gBuffer; + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &descriptorSets.floor)); + writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(descriptorSets.floor, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffers.sceneMatrices.descriptor), + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // SSAO Generation + setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), // FS Position+Depth + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), // FS Normals + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), // FS SSAO Noise + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 3), // FS SSAO Kernel UBO + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 4), // FS Params UBO + }; + setLayoutCreateInfo = vkTools::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast(setLayoutBindings.size())); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &setLayoutCreateInfo, nullptr, &descriptorSetLayouts.ssao)); + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayouts.ssao; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.ssao)); + descriptorAllocInfo.pSetLayouts = &descriptorSetLayouts.ssao; + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &descriptorSets.ssao)); + imageDescriptors = { + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.offscreen.position.view, VK_IMAGE_LAYOUT_GENERAL), + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.offscreen.normal.view, VK_IMAGE_LAYOUT_GENERAL), + }; + writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(descriptorSets.ssao, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &imageDescriptors[0]), // FS Position+Depth + vkTools::initializers::writeDescriptorSet(descriptorSets.ssao, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &imageDescriptors[1]), // FS Normals + vkTools::initializers::writeDescriptorSet(descriptorSets.ssao, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &textures.ssaoNoise.descriptor), // FS SSAO Noise + vkTools::initializers::writeDescriptorSet(descriptorSets.ssao, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3, &uniformBuffers.ssaoKernel.descriptor), // FS SSAO Kernel UBO + vkTools::initializers::writeDescriptorSet(descriptorSets.ssao, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4, &uniformBuffers.ssaoParams.descriptor), // FS SSAO Params UBO + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // SSAO Blur + setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), // FS Sampler SSAO + }; + setLayoutCreateInfo = vkTools::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast(setLayoutBindings.size())); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &setLayoutCreateInfo, nullptr, &descriptorSetLayouts.ssaoBlur)); + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayouts.ssaoBlur; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.ssaoBlur)); + descriptorAllocInfo.pSetLayouts = &descriptorSetLayouts.ssaoBlur; + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &descriptorSets.ssaoBlur)); + // todo + imageDescriptors = { + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.ssao.color.view, VK_IMAGE_LAYOUT_GENERAL), + }; + writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(descriptorSets.ssaoBlur, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &imageDescriptors[0]), + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + + // Composition + setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), // FS Position+Depth + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), // FS Normals + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 2), // FS Albedo + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 3), // FS SSAO + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 4), // FS SSAO blurred + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 5), // FS Lights UBO + }; + setLayoutCreateInfo = vkTools::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), static_cast(setLayoutBindings.size())); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &setLayoutCreateInfo, nullptr, &descriptorSetLayouts.composition)); + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayouts.composition; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.composition)); + descriptorAllocInfo.pSetLayouts = &descriptorSetLayouts.composition; + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorAllocInfo, &descriptorSets.composition)); + imageDescriptors = { + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.offscreen.position.view, VK_IMAGE_LAYOUT_GENERAL), + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.offscreen.normal.view, VK_IMAGE_LAYOUT_GENERAL), + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.offscreen.albedo.view, VK_IMAGE_LAYOUT_GENERAL), + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.ssao.color.view, VK_IMAGE_LAYOUT_GENERAL), + vkTools::initializers::descriptorImageInfo(colorSampler, frameBuffers.ssaoBlur.color.view, VK_IMAGE_LAYOUT_GENERAL), + }; + writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &imageDescriptors[0]), // FS Sampler Position+Depth + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &imageDescriptors[1]), // FS Sampler Normals + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &imageDescriptors[2]), // FS Sampler Albedo + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3, &imageDescriptors[3]), // FS Sampler SSAO + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4, &imageDescriptors[4]), // FS Sampler SSAO blurred + vkTools::initializers::writeDescriptorSet(descriptorSets.composition, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 5, &uniformBuffers.ssaoParams.descriptor), // FS SSAO Params UBO + }; + 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 composition pass pipeline + std::array shaderStages; + + shaderStages[0] = loadShader(getAssetPath() + "shaders/ssao/fullscreen.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/ssao/composition.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vkTools::initializers::pipelineCreateInfo( + pipelineLayouts.composition, + 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(); + + VkPipelineVertexInputStateCreateInfo emptyInputState{}; + emptyInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + emptyInputState.vertexAttributeDescriptionCount = 0; + emptyInputState.pVertexAttributeDescriptions = nullptr; + emptyInputState.vertexBindingDescriptionCount = 0; + emptyInputState.pVertexBindingDescriptions = nullptr; + pipelineCreateInfo.pVertexInputState = &emptyInputState; + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.composition)); + + pipelineCreateInfo.pVertexInputState = &emptyInputState; + + // SSAO Pass + shaderStages[0] = loadShader(getAssetPath() + "shaders/ssao/fullscreen.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/ssao/ssao.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + pipelineCreateInfo.renderPass = frameBuffers.ssao.renderPass; + pipelineCreateInfo.layout = pipelineLayouts.ssao; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.ssao)); + + // SSAO blur pass + shaderStages[0] = loadShader(getAssetPath() + "shaders/ssao/fullscreen.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/ssao/blur.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + pipelineCreateInfo.renderPass = frameBuffers.ssaoBlur.renderPass; + pipelineCreateInfo.layout = pipelineLayouts.ssaoBlur; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.ssaoBlur)); + + // Fill G-Buffer + shaderStages[0] = loadShader(getAssetPath() + "shaders/ssao/gbuffer.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/ssao/gbuffer.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + pipelineCreateInfo.pVertexInputState = &vertices.inputState; + pipelineCreateInfo.renderPass = frameBuffers.offscreen.renderPass; + pipelineCreateInfo.layout = pipelineLayouts.gBuffer; + // Blend attachment states required for all color attachments + // This is important, as color write mask will otherwise be 0x0 and you + // won't see anything rendered to the attachment + std::array blendAttachmentStates = { + 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(); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.offscreen)); + } + + float lerp(float a, float b, float f) + { + return a + f * (b - a); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + // Scene matrices + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.sceneMatrices, + sizeof(uboSceneMatrices)); + + // SSAO parameters + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.ssaoParams, + sizeof(uboSSAOParams)); + + // Update + updateUniformBufferMatrices(); + updateUniformBufferSSAOParams(); + + // SSAO + std::uniform_real_distribution rndDist(0.0f, 1.0f); + std::random_device rndDev; + std::default_random_engine rndGen(rndDev()); + + // Sample kernel + std::vector ssaoKernel(SSAO_KERNEL_SIZE); + for (uint32_t i = 0; i < SSAO_KERNEL_SIZE; ++i) + { + glm::vec3 sample(rndDist(rndGen) * 2.0 - 1.0, rndDist(rndGen) * 2.0 - 1.0, rndDist(rndGen)); + sample = glm::normalize(sample); + sample *= rndDist(rndGen); + float scale = float(i) / float(SSAO_KERNEL_SIZE); + scale = lerp(0.1f, 1.0f, scale * scale); + ssaoKernel[i] = glm::vec4(sample * scale, 0.0f); + } + + // Upload as UBO + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.ssaoKernel, + ssaoKernel.size() * sizeof(glm::vec4), + ssaoKernel.data()); + + // Random noise + std::vector ssaoNoise(SSAO_NOISE_DIM * SSAO_NOISE_DIM); + for (uint32_t i = 0; i < static_cast(ssaoNoise.size()); i++) + { + ssaoNoise[i] = glm::vec4(rndDist(rndGen) * 2.0f - 1.0f, rndDist(rndGen) * 2.0f - 1.0f, 0.0f, 0.0f); + } + // Upload as texture + textureLoader->createTexture(ssaoNoise.data(), ssaoNoise.size() * sizeof(glm::vec4), VK_FORMAT_R32G32B32A32_SFLOAT, SSAO_NOISE_DIM, SSAO_NOISE_DIM, &textures.ssaoNoise, VK_FILTER_LINEAR); + } + + void updateUniformBufferMatrices() + { + uboSceneMatrices.projection = camera.matrices.perspective; + uboSceneMatrices.view = camera.matrices.view; + uboSceneMatrices.model = glm::mat4(); + + VK_CHECK_RESULT(uniformBuffers.sceneMatrices.map()); + uniformBuffers.sceneMatrices.copyTo(&uboSceneMatrices, sizeof(uboSceneMatrices)); + uniformBuffers.sceneMatrices.unmap(); + } + + void updateUniformBufferSSAOParams() + { + uboSSAOParams.projection = camera.matrices.perspective; + + VK_CHECK_RESULT(uniformBuffers.ssaoParams.map()); + uniformBuffers.ssaoParams.copyTo(&uboSSAOParams, sizeof(uboSSAOParams)); + uniformBuffers.ssaoParams.unmap(); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + + // Offscreen rendering + submitInfo.pWaitSemaphores = &semaphores.presentComplete; + submitInfo.pSignalSemaphores = &offscreenSemaphore; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &offScreenCmdBuffer; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + // Scene rendering + submitInfo.pWaitSemaphores = &offscreenSemaphore; + submitInfo.pSignalSemaphores = &semaphores.renderComplete; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadAssets(); + setupVertexDescriptions(); + prepareOffscreenFramebuffers(); + prepareUniformBuffers(); + setupDescriptorPool(); + setupLayoutsAndDescriptors(); + preparePipelines(); + buildCommandBuffers(); + buildDeferredCommandBuffer(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + } + + virtual void viewChanged() + { + updateUniformBufferMatrices(); + updateUniformBufferSSAOParams(); + } + + void toggleSSAO() + { + uboSSAOParams.ssao = !uboSSAOParams.ssao; + updateUniformBufferSSAOParams(); + } + + void toggleSSAOBlur() + { + uboSSAOParams.ssaoBlur = !uboSSAOParams.ssaoBlur; + updateUniformBufferSSAOParams(); + } + + void toggleSSAOOnly() + { + uboSSAOParams.ssaoOnly = !uboSSAOParams.ssaoOnly; + updateUniformBufferSSAOParams(); + } + + virtual void keyPressed(uint32_t keyCode) + { + switch (keyCode) + { + case KEY_F2: + case GAMEPAD_BUTTON_A: + toggleSSAO(); + break; + case KEY_F3: + case GAMEPAD_BUTTON_B: + toggleSSAOBlur(); + break; + case KEY_F4: + case GAMEPAD_BUTTON_Y: + toggleSSAOOnly(); + break; + } + } + + virtual void getOverlayText(VulkanTextOverlay *textOverlay) + { +#if defined(__ANDROID__) + textOverlay->addText("\"Button A\" to toggle SSAO", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("\"Button B\" to toggle SSAO blur", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("\"Button Y\" to toggle SSAO display", 5.0f, 115.0f, VulkanTextOverlay::alignLeft); +#else + textOverlay->addText("\"F2\" to toggle SSAO", 5.0f, 85.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("\"F3\" to toggle SSAO blur", 5.0f, 100.0f, VulkanTextOverlay::alignLeft); + textOverlay->addText("\"F4\" to toggle SSAO display", 5.0f, 115.0f, VulkanTextOverlay::alignLeft); +#endif + } +}; + +VULKAN_EXAMPLE_MAIN() diff --git a/ssao/ssao.vcxproj b/ssao/ssao.vcxproj new file mode 100644 index 00000000..3452d2ee --- /dev/null +++ b/ssao/ssao.vcxproj @@ -0,0 +1,103 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {43FCECF5-F5E1-45DB-972A-73942D459C0A} + 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/ssao/ssao.vcxproj.filters b/ssao/ssao.vcxproj.filters new file mode 100644 index 00000000..74038014 --- /dev/null +++ b/ssao/ssao.vcxproj.filters @@ -0,0 +1,65 @@ + + + + + {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 + + + {08257928-84ac-4091-98b2-2187fb618654} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/vulkanExamples.sln b/vulkanExamples.sln index 57b82921..3109b73a 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -89,6 +89,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Base", "Base", "{09B9A54B-F base\vulkanframebuffer.hpp = base\vulkanframebuffer.hpp base\vulkanheightmap.hpp = base\vulkanheightmap.hpp base\vulkanMeshLoader.hpp = base\vulkanMeshLoader.hpp + base\vulkanscene.hpp = base\vulkanscene.hpp base\vulkanswapchain.hpp = base\vulkanswapchain.hpp base\vulkantextoverlay.hpp = base\vulkantextoverlay.hpp base\vulkanTextureLoader.hpp = base\vulkanTextureLoader.hpp @@ -118,6 +119,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "computecullandlod", "comput EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subpasses", "subpasses\subpasses.vcxproj", "{DE1DEF4B-307E-45C2-90CC-8023327294E2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssao", "ssao\ssao.vcxproj", "{43FCECF5-F5E1-45DB-972A-73942D459C0A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -292,6 +295,10 @@ Global {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 + {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Debug|x64.ActiveCfg = Debug|x64 + {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Debug|x64.Build.0 = Debug|x64 + {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Release|x64.ActiveCfg = Release|x64 + {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE