From 463ac6fe590a6866b468d7c3220e35d544fc47b5 Mon Sep 17 00:00:00 2001 From: ink-soul Date: Wed, 24 May 2023 16:10:36 +0800 Subject: [PATCH] update irradiance caculation --- CMakeSettings.json | 11 + homework/homework1/homework1.cpp | 965 ++++++++++++++++++++++++++++++- homework/homework1/homework1.h | 32 +- 3 files changed, 999 insertions(+), 9 deletions(-) diff --git a/CMakeSettings.json b/CMakeSettings.json index 7313f2b..c9b700d 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -20,6 +20,17 @@ "cmakeCommandArgs": "", "buildCommandArgs": "", "ctestCommandArgs": "", + "inheritEnvironments": [ "clang_cl_x64_x64" ] + }, + { + "name": "x64-Clang-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", "inheritEnvironments": [ "clang_cl_x64_x64" ], "variables": [] } diff --git a/homework/homework1/homework1.cpp b/homework/homework1/homework1.cpp index 6083a9c..4b498c1 100644 --- a/homework/homework1/homework1.cpp +++ b/homework/homework1/homework1.cpp @@ -830,7 +830,7 @@ void VulkanExample::getEnabledFeatures() } } - void VulkanExample::loadglTFFile(std::string filename) + void VulkanExample::loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag = false) { tinygltf::Model glTFInput; tinygltf::TinyGLTF gltfContext; @@ -900,10 +900,7 @@ void VulkanExample::getEnabledFeatures() size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t); glTFModel.indices.count = static_cast(indexBuffer.size()); - struct StagingBuffer { - VkBuffer buffer; - VkDeviceMemory memory; - } vertexStaging, indexStaging; + // Create host visible staging buffers (source) VK_CHECK_RESULT(vulkanDevice->createBuffer( @@ -969,7 +966,9 @@ void VulkanExample::getEnabledFeatures() void VulkanExample::loadAssets() { - loadglTFFile(getAssetPath() + "buster_drone/busterDrone.gltf"); + loadglTFFile(getAssetPath() + "buster_drone/busterDrone.gltf",glTFModel); + loadglTFFile(getAssetPath() + "models/cube.gltf", skyboxModel, true); + ibltextures.skyboxCube.loadFromFile(getAssetPath() + "textures/hdr/pisa_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); } void VulkanExample::setupDescriptors() @@ -1056,7 +1055,6 @@ void VulkanExample::getEnabledFeatures() vks::initializers::writeDescriptorSet(descriptorSet,VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,1,&ibltextures.irradianceCube.descriptor), vks::initializers::writeDescriptorSet(descriptorSet,VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,2,&ibltextures.lutBrdf.descriptor), vks::initializers::writeDescriptorSet(descriptorSet,VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,3,&ibltextures.prefilteredCube.descriptor), - }; vkUpdateDescriptorSets(device, 4, writeDescriptorSets.data(), 0, nullptr); @@ -1256,7 +1254,955 @@ void VulkanExample::getEnabledFeatures() memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values)); } - //-------------------------- pbr precompute start ---------------------------------- +// --------- BRDF LUT precompute preparation ---------------- + void VulkanExample::generateIrradianceCubemap() + { + auto tStart = std::chrono::high_resolution_clock::now(); + + constexpr VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; + constexpr int32_t dim = 64; + const uint32_t numMips = static_cast(floor(log2(dim))) + 1; + + VkImageCreateInfo imageCI = vks::initializers::imageCreateInfo(); + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = format; + imageCI.extent.width = dim; + imageCI.extent.height = dim; + imageCI.extent.depth = 1; + imageCI.mipLevels = numMips; + imageCI.arrayLayers = 6; + imageCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &ibltextures.irradianceCube.image)) + + // allocate memory for irradiance cube map + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, ibltextures.irradianceCube.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, &ibltextures.irradianceCube.deviceMemory)) + VK_CHECK_RESULT(vkBindImageMemory(device, ibltextures.irradianceCube.image, ibltextures.irradianceCube.deviceMemory, 0)) + + + VkImageViewCreateInfo viewCI = vks::initializers::imageViewCreateInfo(); + viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + viewCI.format = format; + viewCI.subresourceRange = {}; + viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewCI.subresourceRange.levelCount = numMips; + viewCI.subresourceRange.layerCount = 6; + viewCI.image = ibltextures.irradianceCube.image; + VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &ibltextures.irradianceCube.view)) + + // set up sampler and image view + VkSamplerCreateInfo samplerCI = vks::initializers::samplerCreateInfo(); + samplerCI.magFilter = VK_FILTER_LINEAR; + samplerCI.minFilter = VK_FILTER_LINEAR; + samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.minLod = 0.0f; + samplerCI.maxLod = static_cast(numMips); + samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &ibltextures.irradianceCube.sampler)) + + ibltextures.irradianceCube.descriptor.imageView = ibltextures.irradianceCube.view; + ibltextures.irradianceCube.descriptor.sampler = ibltextures.irradianceCube.sampler; + ibltextures.irradianceCube.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ibltextures.irradianceCube.device = vulkanDevice; + + + //Setup Framebuffer and so on + VkAttachmentDescription attachDescription = {}; + attachDescription.format = format; + attachDescription.samples = VK_SAMPLE_COUNT_1_BIT; + attachDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + + 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; + //set up render pass + VkRenderPassCreateInfo renderPassCI = vks::initializers::renderPassCreateInfo(); + renderPassCI.attachmentCount = 1; + renderPassCI.pAttachments = &attachDescription; + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + VkRenderPass renderpass; + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass)); + + // create offscreen image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent.width = dim; + imageCreateInfo.extent.height = dim; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &offscreen.image)) + + // allocate memory + VkMemoryAllocateInfo imageCIMemAlloc = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements imageCIMemReqs; + vkGetImageMemoryRequirements(device, offscreen.image, &imageCIMemReqs); + imageCIMemAlloc.allocationSize = imageCIMemReqs.size; + imageCIMemAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(imageCIMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &imageCIMemAlloc, nullptr, &offscreen.memory)) + VK_CHECK_RESULT(vkBindImageMemory(device, offscreen.image, offscreen.memory, 0)) + + // create color image view + VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = format; + colorImageView.flags = 0; + colorImageView.subresourceRange = {}; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = offscreen.image; + VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreen.view)) + + // set up framebuffer for offscreen image + VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = renderpass; + fbufCreateInfo.attachmentCount = 1; + fbufCreateInfo.pAttachments = &offscreen.view; + fbufCreateInfo.width = dim; + fbufCreateInfo.height = dim; + fbufCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreen.framebuffer)) + + VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout( + layoutCmd, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + vulkanDevice->flushCommandBuffer(layoutCmd, queue, true); + + // create descriptor set layout + VkDescriptorSetLayout descriptorsetlayout; + std::vector setLayoutBindings = + { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), + }; + VkDescriptorSetLayoutCreateInfo descriptorsetlayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorsetlayoutCI, nullptr, &descriptorsetlayout)); + // allocate pool + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; + VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + VkDescriptorPool descriptorpool; + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorpool)); + //write to poos + VkDescriptorSet descriptorset; + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorpool, &descriptorsetlayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorset)); + VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(descriptorset, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &ibltextures.skyboxCube.descriptor); + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); + // push matrix + VkPipelineLayout pipelinelayout; + std::vector pushConstantRanges = + { + vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(IrradiancePushBlock), 0) + }; + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorsetlayout, 1); + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = pushConstantRanges.data(); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout)); + + //Pipeline Setting + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array shaderStages; + + const std::vector vertexInputBindings = + { + vks::initializers::vertexInputBindingDescription(0, sizeof(VulkanglTFModel::Vertex), VK_VERTEX_INPUT_RATE_VERTEX), + }; + + const std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, pos)), // Location 0: Position + }; + VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputStateCI.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size()); + vertexInputStateCI.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelinelayout, renderpass); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStages.data(); + pipelineCI.renderPass = renderpass; + + pipelineCI.pVertexInputState = &vertexInputStateCI; + shaderStages[0] = loadShader(getHomeworkShadersPath() + "homework1/filtercube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getHomeworkShadersPath() + "homework1/irradiancecube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VkPipeline pipeline; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + + // offscreen Render pass begin + VkClearValue clearValues[1]; + clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderpass; + renderPassBeginInfo.framebuffer = offscreen.framebuffer; + renderPassBeginInfo.renderArea.extent.width = dim; + renderPassBeginInfo.renderArea.extent.height = dim; + renderPassBeginInfo.clearValueCount = 1; + renderPassBeginInfo.pClearValues = clearValues; + + //six face in cube map + std::vector matrices = { + // POSITIVE_X + glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_X + glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // POSITIVE_Y + glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_Y + glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // POSITIVE_Z + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_Z + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + }; + VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkViewport viewport = vks::initializers::viewport((float)dim, (float)dim, 0.0f, 1.0f); + VkRect2D scissor = vks::initializers::rect2D(dim, dim, 0, 0); + + vkCmdSetViewport(cmdBuf, 0, 1, &viewport); + vkCmdSetScissor(cmdBuf, 0, 1, &scissor); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = numMips; + subresourceRange.layerCount = 6; + + vks::tools::setImageLayout( + cmdBuf, + ibltextures.irradianceCube.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + for (uint32_t m = 0; m < numMips; ++m) + { + for (uint32_t f = 0; f < 6; ++f) + { + viewport.width = static_cast(dim * std::pow(0.5f, m)); + viewport.height = static_cast(dim * std::pow(0.5f, m)); + vkCmdSetViewport(cmdBuf, 0, 1, &viewport); + // Render scene from cube face's point of view + vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + irradinacePushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; + vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(IrradiancePushBlock), &irradinacePushBlock); + + vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL); + skyboxModel.draw(cmdBuf, pipelinelayout); + vkCmdEndRenderPass(cmdBuf); + + vks::tools::setImageLayout( + cmdBuf, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + VkImageCopy copyRegion = {}; + copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.srcSubresource.layerCount = 1; + copyRegion.srcSubresource.mipLevel = 0; + copyRegion.srcSubresource.baseArrayLayer = 0; + + copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.dstSubresource.layerCount = 1; + copyRegion.dstSubresource.mipLevel = m; + copyRegion.dstSubresource.baseArrayLayer = f; + + copyRegion.extent.width = static_cast(viewport.width); + copyRegion.extent.height = static_cast(viewport.height); + copyRegion.extent.depth = 1; + + vkCmdCopyImage( + cmdBuf, + offscreen.image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + ibltextures.irradianceCube.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©Region); + vks::tools::setImageLayout(cmdBuf, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + ); + } + } + + vks::tools::setImageLayout(cmdBuf, + ibltextures.irradianceCube.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + subresourceRange); + vulkanDevice->flushCommandBuffer(cmdBuf, queue); + + vkDestroyRenderPass(device, renderpass, nullptr); + vkDestroyFramebuffer(device, offscreen.framebuffer, nullptr); + vkFreeMemory(device, offscreen.memory, nullptr); + vkDestroyImageView(device, offscreen.view, nullptr); + vkDestroyImage(device, offscreen.image, nullptr); + vkDestroyDescriptorPool(device, descriptorpool, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelinelayout, nullptr); + + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + std::cout << "Generating irradiance cube with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; + + } + + + void VulkanExample::generatePrefilteredCubemap() + { + auto tStart = std::chrono::high_resolution_clock::now(); + + constexpr VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; + constexpr int32_t dim = 512; + const uint32_t numMips = static_cast(floor(log2(dim))) + 1; + + VkImageCreateInfo imageCI = vks::initializers::imageCreateInfo(); + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = format; + imageCI.extent.width = dim; + imageCI.extent.height = dim; + imageCI.extent.depth = 1; + imageCI.mipLevels = numMips; + imageCI.arrayLayers = 6; + imageCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &ibltextures.prefilteredCube.image)); + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, ibltextures.prefilteredCube.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, &ibltextures.prefilteredCube.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device, ibltextures.prefilteredCube.image, ibltextures.prefilteredCube.deviceMemory, 0)); + + // Image view + VkImageViewCreateInfo viewCI = vks::initializers::imageViewCreateInfo(); + viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + viewCI.format = format; + viewCI.subresourceRange = {}; + viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewCI.subresourceRange.levelCount = numMips; + viewCI.subresourceRange.layerCount = 6; + viewCI.image = ibltextures.prefilteredCube.image; + VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &ibltextures.prefilteredCube.view)); + + // Sampler + VkSamplerCreateInfo samplerCI = vks::initializers::samplerCreateInfo(); + samplerCI.magFilter = VK_FILTER_LINEAR; + samplerCI.minFilter = VK_FILTER_LINEAR; + samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.minLod = 0.0f; + samplerCI.maxLod = static_cast(numMips); + samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &ibltextures.prefilteredCube.sampler)); + + ibltextures.prefilteredCube.descriptor.imageView = ibltextures.prefilteredCube.view; + ibltextures.prefilteredCube.descriptor.sampler = ibltextures.prefilteredCube.sampler; + ibltextures.prefilteredCube.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ibltextures.prefilteredCube.device = vulkanDevice; + + // FB, Att, RP, Pipe, etc. + VkAttachmentDescription attDesc = {}; + // Color attachment + attDesc.format = format; + attDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + + // Use subpass dependencies for layout transitions + std::array dependencies; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + 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; + + // Renderpass + VkRenderPassCreateInfo renderPassCI = vks::initializers::renderPassCreateInfo(); + renderPassCI.attachmentCount = 1; + renderPassCI.pAttachments = &attDesc; + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + VkRenderPass renderpass; + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass)); + + struct { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + VkFramebuffer framebuffer; + } offscreen; + //framebuffer + { + // Color attachment + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent.width = dim; + imageCreateInfo.extent.height = dim; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &offscreen.image)); + + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, offscreen.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, &offscreen.memory)); + VK_CHECK_RESULT(vkBindImageMemory(device, offscreen.image, offscreen.memory, 0)); + + VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = format; + colorImageView.flags = 0; + colorImageView.subresourceRange = {}; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = offscreen.image; + VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreen.view)); + + VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = renderpass; + fbufCreateInfo.attachmentCount = 1; + fbufCreateInfo.pAttachments = &offscreen.view; + fbufCreateInfo.width = dim; + fbufCreateInfo.height = dim; + fbufCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreen.framebuffer)); + + VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout( + layoutCmd, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + vulkanDevice->flushCommandBuffer(layoutCmd, queue, true); + } + + // Descriptors + VkDescriptorSetLayout descriptorsetlayout; + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), + }; + VkDescriptorSetLayoutCreateInfo descriptorsetlayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorsetlayoutCI, nullptr, &descriptorsetlayout)); + + // Descriptor Pool + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; + VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + VkDescriptorPool descriptorpool; + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorpool)); + + VkDescriptorSet descriptorset; + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorpool, &descriptorsetlayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorset)); + VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(descriptorset, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &ibltextures.skyboxCube.descriptor); + vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); + + + std::vector pushConstantRanges = { + vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PrefilterPushBlock), 0), + }; + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorsetlayout, 1); + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = pushConstantRanges.data(); + VkPipelineLayout pipelinelayout; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout)); + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array shaderStages; + + const std::vector vertexInputBindings = + { + vks::initializers::vertexInputBindingDescription(0, sizeof(VulkanglTFModel::Vertex), VK_VERTEX_INPUT_RATE_VERTEX), + }; + + const std::vector vertexInputAttributes = { + vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, pos)), // Location 0: Position + }; + VkPipelineVertexInputStateCreateInfo vertexInputStateCI = vks::initializers::pipelineVertexInputStateCreateInfo(); + vertexInputStateCI.vertexBindingDescriptionCount = static_cast(vertexInputBindings.size()); + vertexInputStateCI.pVertexBindingDescriptions = vertexInputBindings.data(); + vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); + vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelinelayout, renderpass); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStages.data(); + pipelineCI.renderPass = renderpass; + pipelineCI.pVertexInputState = &vertexInputStateCI; + + shaderStages[0] = loadShader(getHomeworkShadersPath() + "homework1/filtercube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getHomeworkShadersPath() + "homework1/prefilterenvmap.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkPipeline pipeline; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + + //Render & build cmd + VkClearValue clearValues[1]; + clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + // Reuse render pass from example pass + renderPassBeginInfo.renderPass = renderpass; + renderPassBeginInfo.framebuffer = offscreen.framebuffer; + renderPassBeginInfo.renderArea.extent.width = dim; + renderPassBeginInfo.renderArea.extent.height = dim; + renderPassBeginInfo.clearValueCount = 1; + renderPassBeginInfo.pClearValues = clearValues; + + std::vector matrices = { + // POSITIVE_X + glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_X + glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // POSITIVE_Y + glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_Y + glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // POSITIVE_Z + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + // NEGATIVE_Z + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + }; + VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + VkViewport viewport = vks::initializers::viewport((float)dim, (float)dim, 0.0f, 1.0f); + VkRect2D scissor = vks::initializers::rect2D(dim, dim, 0, 0); + + vkCmdSetViewport(cmdBuf, 0, 1, &viewport); + vkCmdSetScissor(cmdBuf, 0, 1, &scissor); + + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = numMips; + subresourceRange.layerCount = 6; + + vks::tools::setImageLayout( + cmdBuf, + ibltextures.prefilteredCube.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + for (uint32_t m = 0; m < numMips; ++m) + { + //mip level according to roughness + prefilterPushBlock.roughness = float(m) / float(numMips - 1); + for (uint32_t f = 0; f < 6; ++f) + { + viewport.width = static_cast(dim * std::pow(0.5f, m)); + viewport.height = static_cast(dim * std::pow(0.5f, m)); + vkCmdSetViewport(cmdBuf, 0, 1, &viewport); + // Render scene from cube face's point of view + vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + // Update shader push constant block + prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; + + vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock); + vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, nullptr); + skyboxModel.draw(cmdBuf, pipelinelayout); + vkCmdEndRenderPass(cmdBuf); + + vks::tools::setImageLayout( + cmdBuf, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + VkImageCopy copyRegion{}; + copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.srcSubresource.baseArrayLayer = 0; + copyRegion.srcSubresource.mipLevel = 0; + copyRegion.srcSubresource.layerCount = 1; + copyRegion.srcOffset = { 0, 0, 0 }; + + copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copyRegion.dstSubresource.baseArrayLayer = f; + copyRegion.dstSubresource.mipLevel = m; + copyRegion.dstSubresource.layerCount = 1; + copyRegion.dstOffset = { 0, 0, 0 }; + + copyRegion.extent.width = static_cast(viewport.width); + copyRegion.extent.height = static_cast(viewport.height); + copyRegion.extent.depth = 1; + + vkCmdCopyImage( + cmdBuf, + offscreen.image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + ibltextures.prefilteredCube.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©Region); + + //Reset frame buffer image layout + vks::tools::setImageLayout( + cmdBuf, + offscreen.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } + } + + //Set format shader read + vks::tools::setImageLayout( + cmdBuf, + ibltextures.prefilteredCube.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + subresourceRange); + + vulkanDevice->flushCommandBuffer(cmdBuf, queue); + + vkDestroyRenderPass(device, renderpass, nullptr); + vkDestroyFramebuffer(device, offscreen.framebuffer, nullptr); + vkFreeMemory(device, offscreen.memory, nullptr); + vkDestroyImageView(device, offscreen.view, nullptr); + vkDestroyImage(device, offscreen.image, nullptr); + vkDestroyDescriptorPool(device, descriptorpool, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr); + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelinelayout, nullptr); + + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + std::cout << "Generating pre-filtered enivornment cube with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; + } + + void VulkanExample::generateBRDFLUT() + { + auto tStart = std::chrono::high_resolution_clock::now(); + + constexpr VkFormat format = VK_FORMAT_R16G16_SFLOAT; + constexpr int32_t dim = 512; + + // Image + VkImageCreateInfo imageCI = vks::initializers::imageCreateInfo(); + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = format; + imageCI.extent.width = dim; + imageCI.extent.height = dim; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &ibltextures.lutBrdf.image)); + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, ibltextures.lutBrdf.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, &ibltextures.lutBrdf.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device, ibltextures.lutBrdf.image, ibltextures.lutBrdf.deviceMemory, 0)); + + // Image view + VkImageViewCreateInfo viewCI = vks::initializers::imageViewCreateInfo(); + viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCI.format = format; + viewCI.subresourceRange = {}; + viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewCI.subresourceRange.levelCount = 1; + viewCI.subresourceRange.layerCount = 1; + viewCI.image = ibltextures.lutBrdf.image; + VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &ibltextures.lutBrdf.view)); + + // Sampler + VkSamplerCreateInfo samplerCI = vks::initializers::samplerCreateInfo(); + samplerCI.magFilter = VK_FILTER_LINEAR; + samplerCI.minFilter = VK_FILTER_LINEAR; + samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCI.minLod = 0.0f; + samplerCI.maxLod = 1.0f; + samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &ibltextures.lutBrdf.sampler)); + + ibltextures.lutBrdf.descriptor.imageView = ibltextures.lutBrdf.view; + ibltextures.lutBrdf.descriptor.sampler = ibltextures.lutBrdf.sampler; + ibltextures.lutBrdf.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ibltextures.lutBrdf.device = vulkanDevice; + + // FB, Att, RP, Pipe, etc. + VkAttachmentDescription attDesc = {}; + // Color attachment + attDesc.format = format; + attDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + + // Use subpass dependencies for layout transitions + std::array dependencies; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + 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; + + // Create the actual renderpass + VkRenderPassCreateInfo renderPassCI = vks::initializers::renderPassCreateInfo(); + renderPassCI.attachmentCount = 1; + renderPassCI.pAttachments = &attDesc; + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + + VkRenderPass renderpass; + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass)); + + VkFramebufferCreateInfo framebufferCI = vks::initializers::framebufferCreateInfo(); + framebufferCI.renderPass = renderpass; + framebufferCI.attachmentCount = 1; + framebufferCI.pAttachments = &ibltextures.lutBrdf.view; + framebufferCI.width = dim; + framebufferCI.height = dim; + framebufferCI.layers = 1; + + VkFramebuffer framebuffer; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffer)); + + // Descriptors + VkDescriptorSetLayout descriptorsetlayout; + std::vector setLayoutBindings = {}; + VkDescriptorSetLayoutCreateInfo descriptorsetlayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorsetlayoutCI, nullptr, &descriptorsetlayout)); + + // Descriptor Pool + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) }; + VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); + VkDescriptorPool descriptorpool; + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorpool)); + + // Descriptor sets + VkDescriptorSet descriptorset; + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorpool, &descriptorsetlayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorset)); + + // Pipeline layout + VkPipelineLayout pipelinelayout; + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorsetlayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout)); + + // Pipeline + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo(); + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelinelayout, renderpass); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = &emptyInputState; + + // Look-up-table (from BRDF) pipeline + shaderStages[0] = loadShader(getHomeworkShadersPath() + "homework1/genbrdflut.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getHomeworkShadersPath() + "homework1/genbrdflut.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VkPipeline pipeline; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + + // Render + VkClearValue clearValues[1]; + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderpass; + renderPassBeginInfo.renderArea.extent.width = dim; + renderPassBeginInfo.renderArea.extent.height = dim; + renderPassBeginInfo.clearValueCount = 1; + renderPassBeginInfo.pClearValues = clearValues; + renderPassBeginInfo.framebuffer = framebuffer; + + VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + VkViewport viewport = vks::initializers::viewport((float)dim, (float)dim, 0.0f, 1.0f); + VkRect2D scissor = vks::initializers::rect2D(dim, dim, 0, 0); + vkCmdSetViewport(cmdBuf, 0, 1, &viewport); + vkCmdSetScissor(cmdBuf, 0, 1, &scissor); + vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdDraw(cmdBuf, 3, 1, 0, 0); + vkCmdEndRenderPass(cmdBuf); + vulkanDevice->flushCommandBuffer(cmdBuf, queue); + + vkQueueWaitIdle(queue); + + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelinelayout, nullptr); + vkDestroyRenderPass(device, renderpass, nullptr); + vkDestroyFramebuffer(device, framebuffer, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr); + vkDestroyDescriptorPool(device, descriptorpool, nullptr); + + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl; +} + +//-------------------------- pbr precompute start ---------------------------------- #pragma region pbr render pass setting @@ -1326,6 +2272,9 @@ void VulkanExample::getEnabledFeatures() { VulkanExampleBase::prepare(); loadAssets(); + generateBRDFLUT(); + generateIrradianceCubemap(); + generatePrefilteredCubemap(); prepareUniformBuffers(); setupDescriptors(); preparePipelines(); diff --git a/homework/homework1/homework1.h b/homework/homework1/homework1.h index 5b6bcf3..5b39c45 100644 --- a/homework/homework1/homework1.h +++ b/homework/homework1/homework1.h @@ -249,6 +249,11 @@ public: vks::Buffer skinSSBO; } shaderData; + struct StagingBuffer { + VkBuffer buffer; + VkDeviceMemory memory; + } vertexStaging, indexStaging; + struct Pipelines { VkPipeline solid; VkPipeline wireframe = VK_NULL_HANDLE; @@ -325,6 +330,28 @@ public: vks::Texture2D lutBrdf; } ibltextures; + struct OffScreen + { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + VkFramebuffer framebuffer; + } offscreen; + + struct IrradiancePushBlock + { + glm::mat4 mvp; + // Sampling deltas + float deltaPhi = (2.0f * float(M_PI)) / 180.0f; + float deltaTheta = (0.5f * float(M_PI)) / 64.0f; + } irradinacePushBlock; + + struct PrefilterPushBlock { + glm::mat4 mvp; + float roughness; + uint32_t numSamples = 32u; + } prefilterPushBlock; + VulkanglTFModel skyboxModel; VulkanExample(); @@ -356,7 +383,7 @@ public: shaderData.buffer.destroy(); shaderData.skinSSBO.destroy(); } - void loadglTFFile(std::string filename); + void loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag = false); virtual void getEnabledFeatures(); void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment* attachment, uint32_t width, uint32_t height); virtual void setupFrameBuffer(); @@ -365,6 +392,9 @@ public: void setupDescriptors(); void preparePipelines(); void prepareToneMappingPipeline(); + void generateIrradianceCubemap(); + void generatePrefilteredCubemap(); + void generateBRDFLUT(); void prepareUniformBuffers(); void updateUniformBuffers(); void prepare();