diff --git a/src/render/PBR.cpp b/src/render/PBR.cpp index e69de29..d799207 100644 --- a/src/render/PBR.cpp +++ b/src/render/PBR.cpp @@ -0,0 +1,796 @@ +#include "PBR.h" + +void PBR::Material::generateCubemap() +{ + enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 }; + + for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) { + + vks::TextureCubeMap cubemap; + + auto tStart = std::chrono::high_resolution_clock::now(); + + VkFormat format; + int32_t dim; + + switch (target) { + case IRRADIANCE: + format = VK_FORMAT_R32G32B32A32_SFLOAT; + dim = 64; + break; + case PREFILTEREDENV: + format = VK_FORMAT_R16G16B16A16_SFLOAT; + dim = 512; + break; + }; + + const uint32_t numMips = static_cast(floor(log2(dim))) + 1; + + // Create target cubemap + { + // Image + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + 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(VulkanBackend::VulkanFoundation::device, &imageCI, nullptr, &cubemap.image)); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(VulkanBackend::VulkanFoundation::device, cubemap.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = VulkanBackend::VulkanFoundation::vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(VulkanBackend::VulkanFoundation::device, &memAllocInfo, nullptr, &cubemap.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(VulkanBackend::VulkanFoundation::device, cubemap.image, cubemap.deviceMemory, 0)); + + // View + VkImageViewCreateInfo viewCI{}; + viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + 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 = cubemap.image; + VK_CHECK_RESULT(vkCreateImageView(VulkanBackend::VulkanFoundation::device, &viewCI, nullptr, &cubemap.view)); + + // Sampler + VkSamplerCreateInfo samplerCI{}; + samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + 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.maxAnisotropy = 1.0f; + samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(VulkanBackend::VulkanFoundation::device, &samplerCI, nullptr, &cubemap.sampler)); + } + + // 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{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + 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(VulkanBackend::VulkanFoundation::device, &renderPassCI, nullptr, &renderpass)); + + + // Create offscreen framebuffer + { + // Image + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + 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.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateImage(VulkanBackend::VulkanFoundation::device, &imageCI, nullptr, &offscreen.image)); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(VulkanBackend::VulkanFoundation::device, offscreen.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = VulkanBackend::VulkanFoundation::vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(VulkanBackend::VulkanFoundation::device, &memAllocInfo, nullptr, &offscreen.memory)); + VK_CHECK_RESULT(vkBindImageMemory(VulkanBackend::VulkanFoundation::device, offscreen.image, offscreen.memory, 0)); + + // View + VkImageViewCreateInfo viewCI{}; + viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCI.format = format; + viewCI.flags = 0; + viewCI.subresourceRange = {}; + viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewCI.subresourceRange.baseMipLevel = 0; + viewCI.subresourceRange.levelCount = 1; + viewCI.subresourceRange.baseArrayLayer = 0; + viewCI.subresourceRange.layerCount = 1; + viewCI.image = offscreen.image; + VK_CHECK_RESULT(vkCreateImageView(VulkanBackend::VulkanFoundation::device, &viewCI, nullptr, &offscreen.view)); + + // Framebuffer + VkFramebufferCreateInfo framebufferCI{}; + framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCI.renderPass = renderpass; + framebufferCI.attachmentCount = 1; + framebufferCI.pAttachments = &offscreen.view; + framebufferCI.width = dim; + framebufferCI.height = dim; + framebufferCI.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(VulkanBackend::VulkanFoundation::device, &framebufferCI, nullptr, &offscreen.framebuffer)); + + VkCommandBuffer layoutCmd = VulkanBackend::VulkanFoundation::vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = offscreen.image; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + vkCmdPipelineBarrier(layoutCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(layoutCmd, VulkanBackend::VulkanFoundation::graphicQueue, true); + } + + // Descriptors + VkDescriptorSetLayout descriptorsetlayout; + VkDescriptorSetLayoutBinding setLayoutBinding = { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; + descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCI.pBindings = &setLayoutBinding; + descriptorSetLayoutCI.bindingCount = 1; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(VulkanBackend::VulkanFoundation::device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout)); + + // Descriptor Pool + VkDescriptorPoolSize poolSize = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }; + VkDescriptorPoolCreateInfo descriptorPoolCI{}; + descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolCI.poolSizeCount = 1; + descriptorPoolCI.pPoolSizes = &poolSize; + descriptorPoolCI.maxSets = 2; + VkDescriptorPool descriptorpool; + VK_CHECK_RESULT(vkCreateDescriptorPool(VulkanBackend::VulkanFoundation::device, &descriptorPoolCI, nullptr, &descriptorpool)); + + // Descriptor sets + VkDescriptorSet descriptorset; + VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; + descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptorSetAllocInfo.descriptorPool = descriptorpool; + descriptorSetAllocInfo.pSetLayouts = &descriptorsetlayout; + descriptorSetAllocInfo.descriptorSetCount = 1; + VK_CHECK_RESULT(vkAllocateDescriptorSets(VulkanBackend::VulkanFoundation::device, &descriptorSetAllocInfo, &descriptorset)); + VkWriteDescriptorSet writeDescriptorSet{}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.dstSet = descriptorset; + writeDescriptorSet.dstBinding = 0; + writeDescriptorSet.pImageInfo = &textures.environmentCube.descriptor; + vkUpdateDescriptorSets(VulkanBackend::VulkanFoundation::device, 1, &writeDescriptorSet, 0, nullptr); + + // Pipeline layout + VkPipelineLayout pipelinelayout; + VkPushConstantRange pushConstantRange{}; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + switch (target) { + case IRRADIANCE: + pushConstantRange.size = sizeof(IrradiancePushBlock); + break; + case PREFILTEREDENV: + pushConstantRange.size = sizeof(PrefilterPushBlock); + break; + }; + + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCI.setLayoutCount = 1; + pipelineLayoutCI.pSetLayouts = &descriptorsetlayout; + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; + VK_CHECK_RESULT(vkCreatePipelineLayout(VulkanBackend::VulkanFoundation::device, &pipelineLayoutCI, nullptr, &pipelinelayout)); + + // Pipeline + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; + inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; + rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; + rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; + rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizationStateCI.lineWidth = 1.0f; + + VkPipelineColorBlendAttachmentState blendAttachmentState{}; + blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + blendAttachmentState.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; + colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCI.attachmentCount = 1; + colorBlendStateCI.pAttachments = &blendAttachmentState; + + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; + depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilStateCI.depthTestEnable = VK_FALSE; + depthStencilStateCI.depthWriteEnable = VK_FALSE; + depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencilStateCI.front = depthStencilStateCI.back; + depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; + + VkPipelineViewportStateCreateInfo viewportStateCI{}; + viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCI.viewportCount = 1; + viewportStateCI.scissorCount = 1; + + VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; + multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicStateCI{}; + dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); + dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); + + // Vertex input state + VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX }; + VkVertexInputAttributeDescription vertexInputAttribute = { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }; + + VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; + vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputStateCI.vertexBindingDescriptionCount = 1; + vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; + vertexInputStateCI.vertexAttributeDescriptionCount = 1; + vertexInputStateCI.pVertexAttributeDescriptions = &vertexInputAttribute; + + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.layout = pipelinelayout; + pipelineCI.renderPass = renderpass; + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pVertexInputState = &vertexInputStateCI; + pipelineCI.pRasterizationState = &rasterizationStateCI; + pipelineCI.pColorBlendState = &colorBlendStateCI; + pipelineCI.pMultisampleState = &multisampleStateCI; + pipelineCI.pViewportState = &viewportStateCI; + pipelineCI.pDepthStencilState = &depthStencilStateCI; + pipelineCI.pDynamicState = &dynamicStateCI; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStages.data(); + pipelineCI.renderPass = renderpass; + + shaderStages[0] = loadShader(VulkanBackend::VulkanFoundation::device, PlumageRender::Setter::filePath.filterVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT); + switch (target) { + case IRRADIANCE: + shaderStages[1] = loadShader(VulkanBackend::VulkanFoundation::device, PlumageRender::Setter::filePath.irradianceFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); + break; + case PREFILTEREDENV: + shaderStages[1] = loadShader(VulkanBackend::VulkanFoundation::device, PlumageRender::Setter::filePath.prefilterEnvmapFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); + break; + }; + VkPipeline pipeline; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(VulkanBackend::VulkanFoundation::device, VulkanBackend::VulkanFoundation::pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + for (auto shaderStage : shaderStages) { + vkDestroyShaderModule(VulkanBackend::VulkanFoundation::device, shaderStage.module, nullptr); + } + + // Render cubemap + VkClearValue clearValues[1]; + clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; + + VkRenderPassBeginInfo renderPassBeginInfo{}; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + 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 = { + 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)), + 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)), + glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), + glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + }; + + VkCommandBuffer cmdBuf = VulkanBackend::VulkanFoundation::vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); + + VkViewport viewport{}; + viewport.width = (float)dim; + viewport.height = (float)dim; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.extent.width = dim; + scissor.extent.height = dim; + + VkImageSubresourceRange subresourceRange{}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = numMips; + subresourceRange.layerCount = 6; + + // Change image layout for all cubemap faces to transfer destination + { + VulkanBackend::VulkanFoundation::vulkanDevice->beginCommandBuffer(cmdBuf); + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = cubemap.image; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(cmdBuf, VulkanBackend::VulkanFoundation::graphicQueue, false); + } + + for (uint32_t m = 0; m < numMips; m++) { + for (uint32_t f = 0; f < 6; f++) { + + VulkanBackend::VulkanFoundation::vulkanDevice->beginCommandBuffer(cmdBuf); + + 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); + vkCmdSetScissor(cmdBuf, 0, 1, &scissor); + + // Render scene from cube face's point of view + vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + // Pass parameters for current pass using a push constant block + switch (target) { + case IRRADIANCE: + irradiancePushBlock.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), &irradiancePushBlock); + break; + case PREFILTEREDENV: + prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; + prefilterPushBlock.roughness = (float)m / (float)(numMips - 1); + vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock); + break; + }; + + vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL); + + VkDeviceSize offsets[1] = { 0 }; + + PlumageRender::renderMain::models.skybox.draw(cmdBuf); + + vkCmdEndRenderPass(cmdBuf); + + VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = numMips; + subresourceRange.layerCount = 6; + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = offscreen.image; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + // Copy region for transfer from framebuffer to cube face + 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, + cubemap.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©Region); + + { + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = offscreen.image; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + } + + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(cmdBuf, VulkanBackend::VulkanFoundation::graphicQueue, false); + } + } + + { + VulkanBackend::VulkanFoundation::vulkanDevice->beginCommandBuffer(cmdBuf); + VkImageMemoryBarrier imageMemoryBarrier{}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.image = cubemap.image; + imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.subresourceRange = subresourceRange; + vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(cmdBuf, VulkanBackend::VulkanFoundation::graphicQueue, false); + } + + + vkDestroyRenderPass(VulkanBackend::VulkanFoundation::device, renderpass, nullptr); + vkDestroyFramebuffer(VulkanBackend::VulkanFoundation::device, offscreen.framebuffer, nullptr); + vkFreeMemory(VulkanBackend::VulkanFoundation::device, offscreen.memory, nullptr); + vkDestroyImageView(VulkanBackend::VulkanFoundation::device, offscreen.view, nullptr); + vkDestroyImage(VulkanBackend::VulkanFoundation::device, offscreen.image, nullptr); + vkDestroyDescriptorPool(VulkanBackend::VulkanFoundation::device, descriptorpool, nullptr); + vkDestroyDescriptorSetLayout(VulkanBackend::VulkanFoundation::device, descriptorsetlayout, nullptr); + vkDestroyPipeline(VulkanBackend::VulkanFoundation::device, pipeline, nullptr); + vkDestroyPipelineLayout(VulkanBackend::VulkanFoundation::device, pipelinelayout, nullptr); + + cubemap.descriptor.imageView = cubemap.view; + cubemap.descriptor.sampler = cubemap.sampler; + cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + cubemap.device = VulkanBackend::VulkanFoundation::vulkanDevice; + + switch (target) { + case IRRADIANCE: + textures.irradianceCube = cubemap; + break; + case PREFILTEREDENV: + textures.prefilteredCube = cubemap; + shaderData.prefilteredCubeMipLevels = static_cast(numMips); + break; + }; + + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; + } +} + +void PBR::Material::generateBRDFLUT() +{ + auto tStart = std::chrono::high_resolution_clock::now(); + + const VkFormat format = VK_FORMAT_R16G16_SFLOAT; + const int32_t dim = 512; + + // Image + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + 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(VulkanBackend::VulkanFoundation::device, &imageCI, nullptr, &textures.lutBrdf.image)); + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(VulkanBackend::VulkanFoundation::device, textures.lutBrdf.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = VulkanBackend::VulkanFoundation::vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(VulkanBackend::VulkanFoundation::device, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(VulkanBackend::VulkanFoundation::device, textures.lutBrdf.image, textures.lutBrdf.deviceMemory, 0)); + + // View + VkImageViewCreateInfo viewCI{}; + viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + 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 = textures.lutBrdf.image; + VK_CHECK_RESULT(vkCreateImageView(VulkanBackend::VulkanFoundation::device, &viewCI, nullptr, &textures.lutBrdf.view)); + + // Sampler + VkSamplerCreateInfo samplerCI{}; + samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + 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.maxAnisotropy = 1.0f; + samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(VulkanBackend::VulkanFoundation::device, &samplerCI, nullptr, &textures.lutBrdf.sampler)); + + // 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{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + 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(VulkanBackend::VulkanFoundation::device, &renderPassCI, nullptr, &renderpass)); + + VkFramebufferCreateInfo framebufferCI{}; + framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCI.renderPass = renderpass; + framebufferCI.attachmentCount = 1; + framebufferCI.pAttachments = &textures.lutBrdf.view; + framebufferCI.width = dim; + framebufferCI.height = dim; + framebufferCI.layers = 1; + + VkFramebuffer framebuffer; + VK_CHECK_RESULT(vkCreateFramebuffer(VulkanBackend::VulkanFoundation::device, &framebufferCI, nullptr, &framebuffer)); + + // Desriptors + VkDescriptorSetLayout descriptorsetlayout; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; + descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(VulkanBackend::VulkanFoundation::device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout)); + + // Pipeline layout + VkPipelineLayout pipelinelayout; + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCI.setLayoutCount = 1; + pipelineLayoutCI.pSetLayouts = &descriptorsetlayout; + VK_CHECK_RESULT(vkCreatePipelineLayout(VulkanBackend::VulkanFoundation::device, &pipelineLayoutCI, nullptr, &pipelinelayout)); + + // Pipeline + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; + inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; + rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; + rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; + rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizationStateCI.lineWidth = 1.0f; + + VkPipelineColorBlendAttachmentState blendAttachmentState{}; + blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + blendAttachmentState.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; + colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCI.attachmentCount = 1; + colorBlendStateCI.pAttachments = &blendAttachmentState; + + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; + depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilStateCI.depthTestEnable = VK_FALSE; + depthStencilStateCI.depthWriteEnable = VK_FALSE; + depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + depthStencilStateCI.front = depthStencilStateCI.back; + depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; + + VkPipelineViewportStateCreateInfo viewportStateCI{}; + viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCI.viewportCount = 1; + viewportStateCI.scissorCount = 1; + + VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; + multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicStateCI{}; + dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); + dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); + + VkPipelineVertexInputStateCreateInfo emptyInputStateCI{}; + emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.layout = pipelinelayout; + pipelineCI.renderPass = renderpass; + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pVertexInputState = &emptyInputStateCI; + pipelineCI.pRasterizationState = &rasterizationStateCI; + pipelineCI.pColorBlendState = &colorBlendStateCI; + pipelineCI.pMultisampleState = &multisampleStateCI; + pipelineCI.pViewportState = &viewportStateCI; + pipelineCI.pDepthStencilState = &depthStencilStateCI; + pipelineCI.pDynamicState = &dynamicStateCI; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStages.data(); + + // Look-up-table (from BRDF) pipeline + shaderStages = { + loadShader(VulkanBackend::VulkanFoundation::device,PlumageRender::Setter::filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), + loadShader(VulkanBackend::VulkanFoundation::device,PlumageRender::Setter::filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) + }; + VkPipeline pipeline; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(VulkanBackend::VulkanFoundation::device, VulkanBackend::VulkanFoundation::pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + for (auto shaderStage : shaderStages) { + vkDestroyShaderModule(VulkanBackend::VulkanFoundation::device, shaderStage.module, nullptr); + } + + // Render + VkClearValue clearValues[1]; + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + + VkRenderPassBeginInfo renderPassBeginInfo{}; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBeginInfo.renderPass = renderpass; + renderPassBeginInfo.renderArea.extent.width = dim; + renderPassBeginInfo.renderArea.extent.height = dim; + renderPassBeginInfo.clearValueCount = 1; + renderPassBeginInfo.pClearValues = clearValues; + renderPassBeginInfo.framebuffer = framebuffer; + + VkCommandBuffer cmdBuf = VulkanBackend::VulkanFoundation::vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport{}; + viewport.width = (float)dim; + viewport.height = (float)dim; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.extent.width = dim; + scissor.extent.height = dim; + + 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); + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(cmdBuf, VulkanBackend::VulkanFoundation::graphicQueue); + + vkQueueWaitIdle(VulkanBackend::VulkanFoundation::graphicQueue); + + vkDestroyPipeline(VulkanBackend::VulkanFoundation::device, pipeline, nullptr); + vkDestroyPipelineLayout(VulkanBackend::VulkanFoundation::device, pipelinelayout, nullptr); + vkDestroyRenderPass(VulkanBackend::VulkanFoundation::device, renderpass, nullptr); + vkDestroyFramebuffer(VulkanBackend::VulkanFoundation::device, framebuffer, nullptr); + vkDestroyDescriptorSetLayout(VulkanBackend::VulkanFoundation::device, descriptorsetlayout, nullptr); + + textures.lutBrdf.descriptor.imageView = textures.lutBrdf.view; + textures.lutBrdf.descriptor.sampler = textures.lutBrdf.sampler; + textures.lutBrdf.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + textures.lutBrdf.device = VulkanBackend::VulkanFoundation::vulkanDevice; + + 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; +} diff --git a/src/render/PBR.h b/src/render/PBR.h index 8c1b429..6f2e686 100644 --- a/src/render/PBR.h +++ b/src/render/PBR.h @@ -1,7 +1,7 @@ #pragma once #include "glm/glm.hpp" #include - +#include "vulkanFoundation.h" namespace PBR @@ -10,7 +10,14 @@ namespace PBR { public: Material(); - ~Material(); + ~Material() + { + textures.environmentCube.destroy(); + textures.irradianceCube.destroy(); + textures.prefilteredCube.destroy(); + textures.lutBrdf.destroy(); + textures.empty.destroy(); + } struct PushConstBlockMaterial { glm::vec4 baseColorFactor; @@ -27,21 +34,64 @@ namespace PBR float roughnessFactor; float alphaMask; float alphaMaskCutoff; - } pushConstBlockMaterial; - + } ; + static PushConstBlockMaterial pushConstBlockMaterial; struct Textures { vks::TextureCubeMap environmentCube; vks::Texture2D empty; vks::Texture2D lutBrdf; vks::TextureCubeMap irradianceCube; vks::TextureCubeMap prefilteredCube; - } textures; + } ; + static Textures textures; enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 }; + struct ShaderData { + glm::vec4 lightDir; + float exposure = 4.5f; + float gamma = 2.2f; + float prefilteredCubeMipLevels; + float scaleIBLAmbient = 1.0f; + float debugViewInputs = 0; + float debugViewEquation = 0; + }; + + static ShaderData shaderData; + + // generate two cube maps + // irradiance cube map + // prefileter environment cube map + void generateCubemap(); + + // generate BRDF integration map for roughness/NdotV + void generateBRDFLUT(); + private: + //cube map generation + 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; + } irradiancePushBlock; + + struct PrefilterPushBlock { + glm::mat4 mvp; + float roughness; + uint32_t numSamples = 32u; + } prefilterPushBlock; diff --git a/src/render/render.cpp b/src/render/render.cpp index 5bccf20..dc4c0f9 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -36,991 +36,6 @@ void PlumageRender::renderMain::framebufferResizeCallback(GLFWwindow* window, in } -void PlumageRender::renderMain::buildCommandBuffers() - { - VkCommandBufferBeginInfo cmdBufferBeginInfo{}; - cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - - VkClearValue clearValues[3]; - if (PlumageRender::Setter::settings.multiSampling) { - clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; - clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; - clearValues[2].depthStencil = { 1.0f, 0 }; - } - else { - clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; - clearValues[1].depthStencil = { 1.0f, 0 }; - } - - VkRenderPassBeginInfo renderPassBeginInfo{}; - renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.renderArea.offset.x = 0; - renderPassBeginInfo.renderArea.offset.y = 0; - renderPassBeginInfo.renderArea.extent.width =PlumageRender::renderMain::settings.width; - renderPassBeginInfo.renderArea.extent.height =PlumageRender::renderMain::settings.height; - renderPassBeginInfo.clearValueCount =PlumageRender::renderMain::settings.multiSampling ? 3 : 2; - renderPassBeginInfo.pClearValues = clearValues; - - - - for (uint32_t i = 0; i < commandBuffers.size(); ++i) { - renderPassBeginInfo.framebuffer = frameBuffers[i]; - - VkCommandBuffer currentCB = commandBuffers[i]; - - VK_CHECK_RESULT(vkBeginCommandBuffer(currentCB, &cmdBufferBeginInfo)); - vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport viewport{}; - viewport.width = (float)PlumageRender::Setter::settings.width; - viewport.height = (float)PlumageRender::Setter::settings.height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(currentCB, 0, 1, &viewport); - - VkRect2D scissor{}; - scissor.extent = {PlumageRender::renderMain::settings.width,PlumageRender::renderMain::settings.height }; - vkCmdSetScissor(currentCB, 0, 1, &scissor); - - VkDeviceSize offsets[1] = { 0 }; - - if (displayBackground) { - vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr); - vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); - models.skybox.draw(currentCB); - } - - glTFModel::Model& model = models.scene; - - vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets); - if (model.indices.buffer != VK_NULL_HANDLE) { - vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); - } - - boundPipeline = VK_NULL_HANDLE; - - // Opaque primitives first - for (auto node : model.nodes) { - renderNode(node, i, glTFModel::Material::ALPHAMODE_OPAQUE); - } - // Alpha masked primitives - for (auto node : model.nodes) { - renderNode(node, i, glTFModel::Material::ALPHAMODE_MASK); - } - // Transparent primitives - // TODO: Correct depth sorting - for (auto node : model.nodes) { - renderNode(node, i, glTFModel::Material::ALPHAMODE_BLEND); - } - - // User interface - if (!PlumageRender::Setter::settings.headless) - { - gui->draw(currentCB); - } - - - vkCmdEndRenderPass(currentCB); - VK_CHECK_RESULT(vkEndCommandBuffer(currentCB)); - - } - } - - - - -void PlumageRender::renderMain::loadEnvironment(std::string filename) - { - std::cout << "Loading environment from " << filename << std::endl; - if (PlumageRender::renderMain::pbrmaterial.textures.environmentCube.image) { - textures.environmentCube.destroy(); - textures.irradianceCube.destroy(); - textures.prefilteredCube.destroy(); - } - textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); - generateCubemaps(); - } - -void PlumageRender::renderMain::loadAssets() - { - const std::string assetpath = getAssetPath(); - - if (std::filesystem::exists(assetpath.c_str())) { - std::string msg = "asset path get " + assetpath; - std::cout << msg << std::endl; - } - else - { - std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is running from correct relative directory!"; - std::cerr << msg << std::endl; - system("pause"); - //exit(-1); - } - - readDirectory(assetpath + "environments", "*.ktx", environments, false); - - textures.empty.loadFromFile(PlumageRender::Setter::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); - - std::string sceneFile =PlumageRender::renderMain::filePath.glTFModelFilePath; - std::string envMapFile =PlumageRender::renderMain::filePath.envMapFilePath; - - loadScene(sceneFile.c_str()); - models.skybox.loadFromFile(PlumageRender::Setter::filePath.skyboxModleFilePath, vulkanDevice, queue); - - loadEnvironment(envMapFile.c_str()); - } - -void PlumageRender::renderMain::setupNodeDescriptorSet(glTFModel::Node* node) - { - /* - This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures) - */ - - } - - -// generate two cube maps -// irradiance cube map -// prefileter environment cube map -// 重构到PBR中 -void PlumageRender::renderMain::generateCubemaps() - { - enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 }; - - for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) { - - vks::TextureCubeMap cubemap; - - auto tStart = std::chrono::high_resolution_clock::now(); - - VkFormat format; - int32_t dim; - - switch (target) { - case IRRADIANCE: - format = VK_FORMAT_R32G32B32A32_SFLOAT; - dim = 64; - break; - case PREFILTEREDENV: - format = VK_FORMAT_R16G16B16A16_SFLOAT; - dim = 512; - break; - }; - - const uint32_t numMips = static_cast(floor(log2(dim))) + 1; - - // Create target cubemap - { - // Image - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - 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, &cubemap.image)); - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, cubemap.image, &memReqs); - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubemap.deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device, cubemap.image, cubemap.deviceMemory, 0)); - - // View - VkImageViewCreateInfo viewCI{}; - viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - 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 = cubemap.image; - VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &cubemap.view)); - - // Sampler - VkSamplerCreateInfo samplerCI{}; - samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - 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.maxAnisotropy = 1.0f; - samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &cubemap.sampler)); - } - - // 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{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - 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)); - - - // Create offscreen framebuffer - { - // Image - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - 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.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &offscreen.image)); - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, offscreen.image, &memReqs); - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &offscreen.memory)); - VK_CHECK_RESULT(vkBindImageMemory(device, offscreen.image, offscreen.memory, 0)); - - // View - VkImageViewCreateInfo viewCI{}; - viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewCI.format = format; - viewCI.flags = 0; - viewCI.subresourceRange = {}; - viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewCI.subresourceRange.baseMipLevel = 0; - viewCI.subresourceRange.levelCount = 1; - viewCI.subresourceRange.baseArrayLayer = 0; - viewCI.subresourceRange.layerCount = 1; - viewCI.image = offscreen.image; - VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &offscreen.view)); - - // Framebuffer - VkFramebufferCreateInfo framebufferCI{}; - framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferCI.renderPass = renderpass; - framebufferCI.attachmentCount = 1; - framebufferCI.pAttachments = &offscreen.view; - framebufferCI.width = dim; - framebufferCI.height = dim; - framebufferCI.layers = 1; - VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &offscreen.framebuffer)); - - VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.image = offscreen.image; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - imageMemoryBarrier.srcAccessMask = 0; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - vkCmdPipelineBarrier(layoutCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(layoutCmd, queue, true); - } - - // Descriptors - VkDescriptorSetLayout descriptorsetlayout; - VkDescriptorSetLayoutBinding setLayoutBinding = { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; - descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutCI.pBindings = &setLayoutBinding; - descriptorSetLayoutCI.bindingCount = 1; - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout)); - - // Descriptor Pool - VkDescriptorPoolSize poolSize = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }; - VkDescriptorPoolCreateInfo descriptorPoolCI{}; - descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptorPoolCI.poolSizeCount = 1; - descriptorPoolCI.pPoolSizes = &poolSize; - descriptorPoolCI.maxSets = 2; - VkDescriptorPool descriptorpool; - VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorpool)); - - // Descriptor sets - VkDescriptorSet descriptorset; - VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; - descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptorSetAllocInfo.descriptorPool = descriptorpool; - descriptorSetAllocInfo.pSetLayouts = &descriptorsetlayout; - descriptorSetAllocInfo.descriptorSetCount = 1; - VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorset)); - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.dstSet = descriptorset; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.pImageInfo = &textures.environmentCube.descriptor; - vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); - - // Pipeline layout - VkPipelineLayout pipelinelayout; - VkPushConstantRange pushConstantRange{}; - pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - - switch (target) { - case IRRADIANCE: - pushConstantRange.size = sizeof(IrradiancePushBlock); - break; - case PREFILTEREDENV: - pushConstantRange.size = sizeof(PrefilterPushBlock); - break; - }; - - VkPipelineLayoutCreateInfo pipelineLayoutCI{}; - pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCI.setLayoutCount = 1; - pipelineLayoutCI.pSetLayouts = &descriptorsetlayout; - pipelineLayoutCI.pushConstantRangeCount = 1; - pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout)); - - // Pipeline - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; - inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; - rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; - rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; - rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizationStateCI.lineWidth = 1.0f; - - VkPipelineColorBlendAttachmentState blendAttachmentState{}; - blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - blendAttachmentState.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; - colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendStateCI.attachmentCount = 1; - colorBlendStateCI.pAttachments = &blendAttachmentState; - - VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; - depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencilStateCI.depthTestEnable = VK_FALSE; - depthStencilStateCI.depthWriteEnable = VK_FALSE; - depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - depthStencilStateCI.front = depthStencilStateCI.back; - depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; - - VkPipelineViewportStateCreateInfo viewportStateCI{}; - viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCI.viewportCount = 1; - viewportStateCI.scissorCount = 1; - - VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; - multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamicStateCI{}; - dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); - dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); - - // Vertex input state - VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX }; - VkVertexInputAttributeDescription vertexInputAttribute = { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }; - - VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; - vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputStateCI.vertexBindingDescriptionCount = 1; - vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; - vertexInputStateCI.vertexAttributeDescriptionCount = 1; - vertexInputStateCI.pVertexAttributeDescriptions = &vertexInputAttribute; - - std::array shaderStages; - - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.layout = pipelinelayout; - pipelineCI.renderPass = renderpass; - pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; - pipelineCI.pVertexInputState = &vertexInputStateCI; - pipelineCI.pRasterizationState = &rasterizationStateCI; - pipelineCI.pColorBlendState = &colorBlendStateCI; - pipelineCI.pMultisampleState = &multisampleStateCI; - pipelineCI.pViewportState = &viewportStateCI; - pipelineCI.pDepthStencilState = &depthStencilStateCI; - pipelineCI.pDynamicState = &dynamicStateCI; - pipelineCI.stageCount = 2; - pipelineCI.pStages = shaderStages.data(); - pipelineCI.renderPass = renderpass; - - shaderStages[0] = loadShader(device,PlumageRender::renderMain::filePath.filterVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT); - switch (target) { - case IRRADIANCE: - shaderStages[1] = loadShader(device,PlumageRender::renderMain::filePath.irradianceFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); - break; - case PREFILTEREDENV: - shaderStages[1] = loadShader(device,PlumageRender::renderMain::filePath.prefilterEnvmapFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); - break; - }; - VkPipeline pipeline; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); - for (auto shaderStage : shaderStages) { - vkDestroyShaderModule(device, shaderStage.module, nullptr); - } - - // Render cubemap - VkClearValue clearValues[1]; - clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; - - VkRenderPassBeginInfo renderPassBeginInfo{}; - renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - 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 = { - 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)), - 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)), - glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), - glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), - glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)), - 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, false); - - VkViewport viewport{}; - viewport.width = (float)dim; - viewport.height = (float)dim; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor{}; - scissor.extent.width = dim; - scissor.extent.height = dim; - - VkImageSubresourceRange subresourceRange{}; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = numMips; - subresourceRange.layerCount = 6; - - // Change image layout for all cubemap faces to transfer destination - { - vulkanDevice->beginCommandBuffer(cmdBuf); - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.image = cubemap.image; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.srcAccessMask = 0; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); - } - - for (uint32_t m = 0; m < numMips; m++) { - for (uint32_t f = 0; f < 6; f++) { - - vulkanDevice->beginCommandBuffer(cmdBuf); - - 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); - vkCmdSetScissor(cmdBuf, 0, 1, &scissor); - - // Render scene from cube face's point of view - vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - // Pass parameters for current pass using a push constant block - switch (target) { - case IRRADIANCE: - irradiancePushBlock.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), &irradiancePushBlock); - break; - case PREFILTEREDENV: - prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; - prefilterPushBlock.roughness = (float)m / (float)(numMips - 1); - vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock); - break; - }; - - vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL); - - VkDeviceSize offsets[1] = { 0 }; - - models.skybox.draw(cmdBuf); - - vkCmdEndRenderPass(cmdBuf); - - VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = numMips; - subresourceRange.layerCount = 6; - - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.image = offscreen.image; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - // Copy region for transfer from framebuffer to cube face - 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, - cubemap.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ©Region); - - { - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.image = offscreen.image; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - } - - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); - } - } - - { - vulkanDevice->beginCommandBuffer(cmdBuf); - VkImageMemoryBarrier imageMemoryBarrier{}; - imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imageMemoryBarrier.image = cubemap.image; - imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - imageMemoryBarrier.subresourceRange = subresourceRange; - vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); - } - - - 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); - - cubemap.descriptor.imageView = cubemap.view; - cubemap.descriptor.sampler = cubemap.sampler; - cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - cubemap.device = vulkanDevice; - - switch (target) { - case IRRADIANCE: - textures.irradianceCube = cubemap; - break; - case PREFILTEREDENV: - textures.prefilteredCube = cubemap; - shaderData.prefilteredCubeMipLevels = static_cast(numMips); - break; - }; - - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; - } - } -// generate BRDF integration map for roughness/NdotV -// 重构到PBR -void PlumageRender::renderMain::generateBRDFLUT() - { - auto tStart = std::chrono::high_resolution_clock::now(); - - const VkFormat format = VK_FORMAT_R16G16_SFLOAT; - const int32_t dim = 512; - - // Image - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - 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, &textures.lutBrdf.image)); - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, textures.lutBrdf.image, &memReqs); - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device, textures.lutBrdf.image, textures.lutBrdf.deviceMemory, 0)); - - // View - VkImageViewCreateInfo viewCI{}; - viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - 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 = textures.lutBrdf.image; - VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &textures.lutBrdf.view)); - - // Sampler - VkSamplerCreateInfo samplerCI{}; - samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - 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.maxAnisotropy = 1.0f; - samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &textures.lutBrdf.sampler)); - - // 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{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - 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{}; - framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferCI.renderPass = renderpass; - framebufferCI.attachmentCount = 1; - framebufferCI.pAttachments = &textures.lutBrdf.view; - framebufferCI.width = dim; - framebufferCI.height = dim; - framebufferCI.layers = 1; - - VkFramebuffer framebuffer; - VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffer)); - - // Desriptors - VkDescriptorSetLayout descriptorsetlayout; - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; - descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout)); - - // Pipeline layout - VkPipelineLayout pipelinelayout; - VkPipelineLayoutCreateInfo pipelineLayoutCI{}; - pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCI.setLayoutCount = 1; - pipelineLayoutCI.pSetLayouts = &descriptorsetlayout; - VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelinelayout)); - - // Pipeline - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; - inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; - rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; - rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; - rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizationStateCI.lineWidth = 1.0f; - - VkPipelineColorBlendAttachmentState blendAttachmentState{}; - blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - blendAttachmentState.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; - colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendStateCI.attachmentCount = 1; - colorBlendStateCI.pAttachments = &blendAttachmentState; - - VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; - depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencilStateCI.depthTestEnable = VK_FALSE; - depthStencilStateCI.depthWriteEnable = VK_FALSE; - depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; - depthStencilStateCI.front = depthStencilStateCI.back; - depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; - - VkPipelineViewportStateCreateInfo viewportStateCI{}; - viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCI.viewportCount = 1; - viewportStateCI.scissorCount = 1; - - VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; - multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamicStateCI{}; - dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); - dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); - - VkPipelineVertexInputStateCreateInfo emptyInputStateCI{}; - emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - - std::array shaderStages; - - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.layout = pipelinelayout; - pipelineCI.renderPass = renderpass; - pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; - pipelineCI.pVertexInputState = &emptyInputStateCI; - pipelineCI.pRasterizationState = &rasterizationStateCI; - pipelineCI.pColorBlendState = &colorBlendStateCI; - pipelineCI.pMultisampleState = &multisampleStateCI; - pipelineCI.pViewportState = &viewportStateCI; - pipelineCI.pDepthStencilState = &depthStencilStateCI; - pipelineCI.pDynamicState = &dynamicStateCI; - pipelineCI.stageCount = 2; - pipelineCI.pStages = shaderStages.data(); - - // Look-up-table (from BRDF) pipeline - shaderStages = { - loadShader(device,PlumageRender::Setter::filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), - loadShader(device,PlumageRender::Setter::filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) - }; - VkPipeline pipeline; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); - for (auto shaderStage : shaderStages) { - vkDestroyShaderModule(device, shaderStage.module, nullptr); - } - - // Render - VkClearValue clearValues[1]; - clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; - - VkRenderPassBeginInfo renderPassBeginInfo{}; - renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - 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{}; - viewport.width = (float)dim; - viewport.height = (float)dim; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor{}; - scissor.extent.width = dim; - scissor.extent.height = dim; - - 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); - - textures.lutBrdf.descriptor.imageView = textures.lutBrdf.view; - textures.lutBrdf.descriptor.sampler = textures.lutBrdf.sampler; - textures.lutBrdf.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - textures.lutBrdf.device = vulkanDevice; - - 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; - } - -// Prepare and initialize uniform buffer containing shader uniforms - - -// 更新统一缓冲区 done -void PlumageRender::renderMain::updateUniformBuffers() - { - // Scene - shaderDataScene.projection = camera.matrices.perspective; - shaderDataScene.view = camera.matrices.view; - - // Center and scale model - float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f; - glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]); - translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]); - - shaderDataScene.model = glm::mat4(1.0f); - shaderDataScene.model[0][0] = scale; - shaderDataScene.model[1][1] = scale; - shaderDataScene.model[2][2] = scale; - shaderDataScene.model = glm::translate(shaderDataScene.model, translate); - - shaderDataScene.camPos = glm::vec3( - -camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)), - -camera.position.z * sin(glm::radians(camera.rotation.x)), - camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)) - ); - - // Skybox - shaderDataSkybox.projection = camera.matrices.perspective; - shaderDataSkybox.view = camera.matrices.view; - shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view)); - } - -void PlumageRender::renderMain::updateShaderData() - { - shaderData.lightDir = glm::vec4( - sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), - sin(glm::radians(lightSource.rotation.y)), - cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), - 0.0f); - } // todo:重写成glfw的 void PlumageRender::renderMain::windowResized() { @@ -1040,12 +55,12 @@ void PlumageRender::renderMain::prepare() loadAssets(); generateBRDFLUT(); generateCubemaps(); - prepareUniformBuffers(); - setupDescriptors(); + //prepareUniformBuffers(); + //setupDescriptors(); preparePipelines(); if (!PlumageRender::Setter::settings.headless) { - gui = new UI(vulkanDevice, renderPass, queue, pipelineCache,PlumageRender::renderMain::settings.sampleCount); + gui = new UI(vulkanDevice, renderPass, queue, pipelineCache,PlumageRender::Setter::settings.sampleCount); updateUIOverlay(); } buildCommandBuffers(); @@ -1077,356 +92,6 @@ void PlumageRender::renderMain::submitWork(VkCommandBuffer cmdBuffer, VkQueue qu vkDestroyFence(device, fence, nullptr); } -// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName -// 移动到fileSystem里 -void PlumageRender::renderMain::writeImageToFile(std::string filePath) - { - - bool screenshotSaved = false; - bool supportsBlit = true; - - // Check blit support for source and destination - VkFormatProperties formatProps; - - // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) - vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps); - if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { - std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; - supportsBlit = false; - } - - // Check if the device supports blitting to linear images - vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); - if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { - std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; - supportsBlit = false; - } - - // Source for the copy is the last rendered swapchain image - VkImage srcImage = swapChain.images[currentBuffer]; - - // Create the linear tiled destination image to copy to and to read the memory from - VkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo()); - imageCreateCI.imageType = VK_IMAGE_TYPE_2D; - // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ - imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM; - imageCreateCI.extent.width =PlumageRender::renderMain::settings.width; - imageCreateCI.extent.height =PlumageRender::renderMain::settings.height; - imageCreateCI.extent.depth = 1; - imageCreateCI.arrayLayers = 1; - imageCreateCI.mipLevels = 1; - imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR; - imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; - // Create the image - VkImage dstImage; - VK_CHECK_RESULT(vkCreateImage(device, &imageCreateCI, nullptr, &dstImage)); - // Create memory to back up the image - VkMemoryRequirements memRequirements; - VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); - VkDeviceMemory dstImageMemory; - vkGetImageMemoryRequirements(device, dstImage, &memRequirements); - memAllocInfo.allocationSize = memRequirements.size; - // Memory must be host visible to copy from - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0)); - - // Do the actual blit from the swapchain image to our host visible destination image - VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Transition destination image to transfer destination layout - vks::tools::insertImageMemoryBarrier( - copyCmd, - dstImage, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - - // Transition swapchain image from present to transfer source layout - vks::tools::insertImageMemoryBarrier( - copyCmd, - srcImage, - VK_ACCESS_MEMORY_READ_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - //VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - - // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) - if (supportsBlit) - { - // Define the region to blit (we will blit the whole swapchain image) - VkOffset3D blitSize; - blitSize.x =PlumageRender::renderMain::settings.width; - blitSize.y =PlumageRender::renderMain::settings.height; - blitSize.z = 1; - VkImageBlit imageBlitRegion{}; - imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBlitRegion.srcSubresource.layerCount = 1; - imageBlitRegion.srcOffsets[1] = blitSize; - imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBlitRegion.dstSubresource.layerCount = 1; - imageBlitRegion.dstOffsets[1] = blitSize; - - // Issue the blit command - vkCmdBlitImage( - copyCmd, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &imageBlitRegion, - VK_FILTER_NEAREST); - } - else - { - // Otherwise use image copy (requires us to manually flip components) - VkImageCopy imageCopyRegion{}; - imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopyRegion.srcSubresource.layerCount = 1; - imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopyRegion.dstSubresource.layerCount = 1; - imageCopyRegion.extent.width =PlumageRender::renderMain::settings.width; - imageCopyRegion.extent.height =PlumageRender::renderMain::settings.height; - imageCopyRegion.extent.depth = 1; - - // Issue the copy command - vkCmdCopyImage( - copyCmd, - srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &imageCopyRegion); - } - - // Transition destination image to general layout, which is the required layout for mapping the image memory later on - vks::tools::insertImageMemoryBarrier( - copyCmd, - dstImage, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_GENERAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - - if (!PlumageRender::Setter::settings.headless) - { - // Transition back the swap chain image after the blit is done - vks::tools::insertImageMemoryBarrier( - copyCmd, - srcImage, - VK_ACCESS_TRANSFER_READ_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - } - - vulkanDevice->flushCommandBuffer(copyCmd, queue); - - // Get layout of the image (including row pitch) - VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; - VkSubresourceLayout subResourceLayout; - vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout); - - // Map image memory so we can start copying from it - const char* data; - vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data); - data += subResourceLayout.offset; - - // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components - bool colorSwizzle = false; - // Check if source is BGR - // Note: Not complete, only contains most common and basic BGR surface formats for demonstration purposes - if (!supportsBlit) - { - std::vector formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; - colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end()); - } - - if (PlumageRender::Setter::settings.outputPNGimage) - { - if (colorSwizzle) - { - // 暂时不改,此处需要将BGR通道改成RGB格式 - stbi_write_png(filePath.c_str(),PlumageRender::renderMain::settings.width,PlumageRender::renderMain::settings.height, 4, data, static_cast(subResourceLayout.rowPitch)); - } - else - { - stbi_write_png(filePath.c_str(),PlumageRender::renderMain::settings.width,PlumageRender::renderMain::settings.height, 4, data, static_cast(subResourceLayout.rowPitch)); - } - - } - else - { - - std::ofstream file(filePath, std::ios::out | std::ios::binary); - - // ppm header - file << "P6\n" <PlumageRender::renderMain::settings.endFrameIndex) - { - // 避免重复改变为true带来的无效性能开销 - if (signal.imageSequenceOutputComplete) - { - return; - } - // 生成结束的信号标志置为true - signal.imageSequenceOutputComplete = true; - // 构造ffmpeg脚本需要的路径变量(提前到配置) - std::string fileName = "/%dresult.ppm"; - PlumageRender::Setter::filePath.totalImageOutputPath =PlumageRender::renderMain::filePath.deviceSpecFilePath + fileName; - return; - } - // 路径存在性检查,不存在则创建 - if (!std::filesystem::exists(PlumageRender::Setter::filePath.deviceSpecFilePath.c_str())) - { - std::filesystem::create_directories(PlumageRender::Setter::filePath.deviceSpecFilePath.c_str()); - } - // 拼接图片序列编号到路径里 - std::string fileName ="/" + std::to_string(savedFrameCounter) + "result.ppm"; - PlumageRender::Setter::filePath.totalImageOutputPath =PlumageRender::renderMain::filePath.deviceSpecFilePath + fileName; - //std::cout << outputPath << std::endl; - // 写入文件 - writeImageToFile(PlumageRender::Setter::filePath.totalImageOutputPath.c_str()); - // 写入一帧后已保存帧数+1 - savedFrameCounter++; - } - -void PlumageRender::renderMain::imageSequenceToVideo() - { - // 边界条件,图片序列输出未完成 - if (!signal.imageSequenceOutputComplete) - { - return; - } - // 边界条件,图片序列到视频的输出已完成 - if (signal.imageSequenceToVideoComplete) - { - return; - } - // 拼接视频保存的设备编号路径(提前到配置文件进行) - std::string deviceFilePath =PlumageRender::renderMain::filePath.videoOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex); - // 判断路径是否存在,不存在则创建 - if (std::filesystem::exists(deviceFilePath.c_str())) - { - std::filesystem::create_directories(deviceFilePath.c_str()); - } - // 构造结果视频路径 - std::string resultVideoPath = deviceFilePath + "/result.mp4"; - // 构造脚本需要参数,图片序列路径和设定的帧率 - std::string commandLineImageSequencePath =PlumageRender::renderMain::filePath.totalImageOutputPath; - //std::string commandLineCodecAndResultPath = resultVideoPath; - std::string commandLineFrameRate = std::to_string(PlumageRender::renderMain::settings.videoFrameRate); - // 根据不同系统使用不同脚本 -#if defined(_WIN32) - std::string commandLine =PlumageRender::renderMain::filePath.image2videoBatFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath; -#else - std::string commandLine = filePath.image2videoShFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath; -#endif - std::cout << commandLine << std::endl; - std::system(commandLine.c_str()); - // 视频输出完成,置标志为true - signal.imageSequenceToVideoComplete = true; - std::cout << "vidoe codec complete,saved in:" << resultVideoPath << std::endl; - std::cout << "star to clean up image sequence" << std::endl; - removeImageSequence(); - } - -void PlumageRender::renderMain::removeImageSequence() - { - // 函数非第一次运行的边界条件 - if (savedFrameCounter !=PlumageRender::renderMain::settings.startFrameCount) - { - // 检查视频输出完成的标志位 - if (!signal.imageSequenceToVideoComplete) - { - return; - } - - } - // 遍历删除图片序列文件和空文件夹 - if (std::filesystem::exists(PlumageRender::Setter::filePath.deviceSpecFilePath)) - { - for (const auto& entry : std::filesystem::directory_iterator(PlumageRender::Setter::filePath.deviceSpecFilePath)) - { - if (std::filesystem::is_directory(entry.path())) - { - std::filesystem::remove_all(entry.path()); - } - else - { - std::filesystem::remove(entry.path()); - } - } - std::filesystem::remove(PlumageRender::Setter::filePath.deviceSpecFilePath); - std::cout << "clean up complete" << std::endl; - } - return; - - } - - - void PlumageRender::renderMain::render() { if (!prepared) { @@ -1528,12 +193,8 @@ void PlumageRender::renderMain::fileDropped(std::string filename) buildCommandBuffers(); } -// 重构到单独的UI里 -void PlumageRender::renderMain::updateUIOverlay() - { - - } + @@ -1571,14 +232,16 @@ PlumageRender* plumageRender; int main() { - PlumageRender::renderMain* plumageRender; - PlumageRender::Setter setter; + PlumageRender::renderMain plumageRender; + VulkanBackend::VulkanFoundation vkFoundation; + + vkFoundation.initVulkan(); if (!PlumageRender::Setter::settings.headless) { - plumageRender->initWindow(PlumageRender::Setter::settings.width,PlumageRender::renderMain::settings.height); + plumageRender.initWindow(PlumageRender::Setter::settings.width,PlumageRender::Setter::settings.height); } - plumageRender->initVulkan(); + return 0; } diff --git a/src/render/render.h b/src/render/render.h index 3303a48..a23a522 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -30,23 +30,19 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" +#include "vulkanFoundation.h" #define ENABLE_VALIDATION false namespace PlumageRender { - class renderMain : public VulkanExampleBase + class renderMain { public: GLFWwindow* window; - static VulkanBackend::VulkanFoundation vkFoundation; - static PlumageRender::Setter::Settings settings; - static PlumageRender::Setter::FilePath filePath; - static PBR::Material pbrmaterial; - static PlumageRender::RenderInput renderInput; - static PlumageRender::RenderOutput renderOutput; + static Camera camera; struct Models @@ -65,120 +61,40 @@ namespace PlumageRender glm::vec3 modelrot = glm::vec3(0.0f); glm::vec3 modelPos = glm::vec3(0.0f); - - uint32_t frameIndex = 0; //VkImage swapChainImage; + static uint32_t currentBuffer; - struct LightSource { - glm::vec3 color = glm::vec3(1.0f); - glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f); - } lightSource; - - //cube map generation - - 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; - } irradiancePushBlock; - - struct PrefilterPushBlock { - glm::mat4 mvp; - float roughness; - uint32_t numSamples = 32u; - } prefilterPushBlock; UI* gui; - uint64_t savedFrameCounter = settings.startFrameCount; + uint64_t savedFrameCounter = PlumageRender::Setter::settings.startFrameCount; bool framebufferResized = false; renderMain(); ~renderMain() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipelines.skybox, nullptr); - vkDestroyPipeline(device, pipelines.pbr, nullptr); - vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr); + models.scene.destroy(VulkanBackend::VulkanFoundation::device); + models.skybox.destroy(VulkanBackend::VulkanFoundation::device); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); - - models.scene.destroy(device); - models.skybox.destroy(device); - - for (auto buffer : uniformBuffers) { - buffer.params.destroy(); - buffer.scene.destroy(); - buffer.skybox.destroy(); - } - for (auto fence : waitFences) { - vkDestroyFence(device, fence, nullptr); - } - for (auto semaphore : renderCompleteSemaphores) { - vkDestroySemaphore(device, semaphore, nullptr); - } - for (auto semaphore : presentCompleteSemaphores) { - vkDestroySemaphore(device, semaphore, nullptr); - } - - textures.environmentCube.destroy(); - textures.irradianceCube.destroy(); - textures.prefilteredCube.destroy(); - textures.lutBrdf.destroy(); - textures.empty.destroy(); + delete gui; } void initWindow(int width, int height); - static void framebufferResizeCallback(GLFWwindow* window, int width, int height); - void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode); - void loadScene(std::string filename); - void loadEnvironment(std::string filename); - void buildCommandBuffers(); - void loadAssets(); - void setupNodeDescriptorSet(glTFModel::Node* node); - void setupDescriptors(); - void preparePipelines(); - // void tonemappingPipelin(); - void generateCubemaps(); - void generateBRDFLUT(); - void prepareUniformBuffers(); - void updateUniformBuffers(); - void updateShaderData(); void windowResized(); void prepare(); void setupCamera(); void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue); - - void writeImageToFile(std::string filePath); - void outputImageSequence(); - void imageSequenceToVideo(); - void removeImageSequence(); - //void outputScreenShot(); - //uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties); virtual void render(); - virtual void updateUIOverlay(); virtual void fileDropped(std::string filename); + private: }; + } diff --git a/src/render/renderIO.cpp b/src/render/renderIO.cpp index 37af4cb..3b3ef92 100644 --- a/src/render/renderIO.cpp +++ b/src/render/renderIO.cpp @@ -1,14 +1,14 @@ #include "renderIO.h" -void PlumageRender::RenderInput::loadScene() +void PlumageRender::RenderInput::loadScene(std::string fileName) { - std::string filename = PlumageRender::renderMain::filePath.glTFModelFilePath; - std::cout << "Loading scene from " << filename << std::endl; - PlumageRender::renderMain::models.scene.destroy(PlumageRender::renderMain::vkFoundation.device); + + std::cout << "Loading scene from " << fileName << std::endl; + PlumageRender::renderMain::models.scene.destroy(VulkanBackend::VulkanFoundation::device); animationIndex = 0; animationTimer = 0.0f; auto tStart = std::chrono::high_resolution_clock::now(); - PlumageRender::renderMain::models.scene.loadFromFile(filename, PlumageRender::renderMain::vkFoundation.vulkanDevice, PlumageRender::renderMain::vkFoundation.graphicQueue); + PlumageRender::renderMain::models.scene.loadFromFile(fileName, VulkanBackend::VulkanFoundation::vulkanDevice, VulkanBackend::VulkanFoundation::graphicQueue); auto tFileLoad = std::chrono::duration(std::chrono::high_resolution_clock::now() - tStart).count(); std::cout << "Loading took " << tFileLoad << " ms" << std::endl; PlumageRender::renderMain::camera.setPosition({ 0.0f, 0.0f, -1.0f }); @@ -17,5 +17,391 @@ void PlumageRender::RenderInput::loadScene() void PlumageRender::RenderInput::loadEnvironment(std::string fileName) { + std::cout << "Loading environment from " << fileName << std::endl; + if (PBR::Material::textures.environmentCube.image) { + PBR::Material::textures.environmentCube.destroy(); + PBR::Material::textures.irradianceCube.destroy(); + PBR::Material::textures.prefilteredCube.destroy(); + } + PBR::Material::textures.environmentCube.loadFromFile(fileName, VK_FORMAT_R16G16B16A16_SFLOAT, VulkanBackend::VulkanFoundation::vulkanDevice, VulkanBackend::VulkanFoundation::graphicQueue); + PBR::Material material; + material.generateCubemap(); +} + +void PlumageRender::RenderInput::loadAssets() +{ + const std::string assetpath = getAssetPath(); + + if (std::filesystem::exists(assetpath.c_str())) { + std::string msg = "asset path get " + assetpath; + std::cout << msg << std::endl; + } + else + { + std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is running from correct relative directory!"; + std::cerr << msg << std::endl; + system("pause"); + //exit(-1); + } + + readDirectory(assetpath + "environments", "*.ktx", environments, false); + + PBR::Material::textures.empty.loadFromFile(PlumageRender::Setter::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, VulkanBackend::VulkanFoundation::vulkanDevice, VulkanBackend::VulkanFoundation::graphicQueue); + + std::string sceneFile = PlumageRender::Setter::filePath.glTFModelFilePath; + std::string envMapFile = PlumageRender::Setter::filePath.envMapFilePath; + + loadScene(sceneFile.c_str()); + PlumageRender::renderMain::models.skybox.loadFromFile(PlumageRender::Setter::filePath.skyboxModleFilePath, VulkanBackend::VulkanFoundation::vulkanDevice, VulkanBackend::VulkanFoundation::graphicQueue); + + loadEnvironment(envMapFile.c_str()); +} + +// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName +// 移动到fileSystem里 +void PlumageRender::RenderOutput::writeImageToFile(std::string filePath) +{ + + bool screenshotSaved = false; + bool supportsBlit = true; + + // Check blit support for source and destination + VkFormatProperties formatProps; + + // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) + vkGetPhysicalDeviceFormatProperties(VulkanBackend::VulkanFoundation::physicalDevice, VulkanBackend::VulkanFoundation::swapChainImageFormat, &formatProps); + if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { + std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; + supportsBlit = false; + } + + // Check if the device supports blitting to linear images + vkGetPhysicalDeviceFormatProperties(VulkanBackend::VulkanFoundation::physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); + if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { + std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl; + supportsBlit = false; + } + + // Source for the copy is the last rendered swapchain image + VkImage srcImage = VulkanBackend::VulkanFoundation::swapChainImages[PlumageRender::renderMain::currentBuffer]; + + // Create the linear tiled destination image to copy to and to read the memory from + VkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo()); + imageCreateCI.imageType = VK_IMAGE_TYPE_2D; + // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ + imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateCI.extent.width = PlumageRender::Setter::settings.width; + imageCreateCI.extent.height = PlumageRender::Setter::settings.height; + imageCreateCI.extent.depth = 1; + imageCreateCI.arrayLayers = 1; + imageCreateCI.mipLevels = 1; + imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + // Create the image + VkImage dstImage; + VK_CHECK_RESULT(vkCreateImage(VulkanBackend::VulkanFoundation::device, &imageCreateCI, nullptr, &dstImage)); + // Create memory to back up the image + VkMemoryRequirements memRequirements; + VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo()); + VkDeviceMemory dstImageMemory; + vkGetImageMemoryRequirements(VulkanBackend::VulkanFoundation::device, dstImage, &memRequirements); + memAllocInfo.allocationSize = memRequirements.size; + // Memory must be host visible to copy from + memAllocInfo.memoryTypeIndex = VulkanBackend::VulkanFoundation::vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VK_CHECK_RESULT(vkAllocateMemory(VulkanBackend::VulkanFoundation::device, &memAllocInfo, nullptr, &dstImageMemory)); + VK_CHECK_RESULT(vkBindImageMemory(VulkanBackend::VulkanFoundation::device, dstImage, dstImageMemory, 0)); + + // Do the actual blit from the swapchain image to our host visible destination image + VkCommandBuffer copyCmd = VulkanBackend::VulkanFoundation::vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Transition destination image to transfer destination layout + vks::tools::insertImageMemoryBarrier( + copyCmd, + dstImage, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + + // Transition swapchain image from present to transfer source layout + vks::tools::insertImageMemoryBarrier( + copyCmd, + srcImage, + VK_ACCESS_MEMORY_READ_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + //VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + + // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) + if (supportsBlit) + { + // Define the region to blit (we will blit the whole swapchain image) + VkOffset3D blitSize; + blitSize.x = PlumageRender::Setter::settings.width; + blitSize.y = PlumageRender::Setter::settings.height; + blitSize.z = 1; + VkImageBlit imageBlitRegion{}; + imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.srcSubresource.layerCount = 1; + imageBlitRegion.srcOffsets[1] = blitSize; + imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBlitRegion.dstSubresource.layerCount = 1; + imageBlitRegion.dstOffsets[1] = blitSize; + + // Issue the blit command + vkCmdBlitImage( + copyCmd, + srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageBlitRegion, + VK_FILTER_NEAREST); + } + else + { + // Otherwise use image copy (requires us to manually flip components) + VkImageCopy imageCopyRegion{}; + imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.srcSubresource.layerCount = 1; + imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopyRegion.dstSubresource.layerCount = 1; + imageCopyRegion.extent.width = PlumageRender::Setter::settings.width; + imageCopyRegion.extent.height = PlumageRender::Setter::settings.height; + imageCopyRegion.extent.depth = 1; + + // Issue the copy command + vkCmdCopyImage( + copyCmd, + srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopyRegion); + } + + // Transition destination image to general layout, which is the required layout for mapping the image memory later on + vks::tools::insertImageMemoryBarrier( + copyCmd, + dstImage, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + + if (!PlumageRender::Setter::settings.headless) + { + // Transition back the swap chain image after the blit is done + vks::tools::insertImageMemoryBarrier( + copyCmd, + srcImage, + VK_ACCESS_TRANSFER_READ_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + } + + VulkanBackend::VulkanFoundation::vulkanDevice->flushCommandBuffer(copyCmd, VulkanBackend::VulkanFoundation::graphicQueue); + + // Get layout of the image (including row pitch) + VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; + VkSubresourceLayout subResourceLayout; + vkGetImageSubresourceLayout(VulkanBackend::VulkanFoundation::device, dstImage, &subResource, &subResourceLayout); + + // Map image memory so we can start copying from it + const char* data; + vkMapMemory(VulkanBackend::VulkanFoundation::device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data); + data += subResourceLayout.offset; + + // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components + bool colorSwizzle = false; + // Check if source is BGR + // Note: Not complete, only contains most common and basic BGR surface formats for demonstration purposes + if (!supportsBlit) + { + std::vector formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; + colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), VulkanBackend::VulkanFoundation::swapChainImageFormat) != formatsBGR.end()); + } + + if (PlumageRender::Setter::settings.outputPNGimage) + { + if (colorSwizzle) + { + // 暂时不改,此处需要将BGR通道改成RGB格式 + stbi_write_png(filePath.c_str(), PlumageRender::Setter::settings.width, PlumageRender::Setter::settings.height, 4, data, static_cast(subResourceLayout.rowPitch)); + } + else + { + stbi_write_png(filePath.c_str(), PlumageRender::Setter::settings.width, PlumageRender::Setter::settings.height, 4, data, static_cast(subResourceLayout.rowPitch)); + } + + } + else + { + + std::ofstream file(filePath, std::ios::out | std::ios::binary); + + // ppm header + file << "P6\n" << PlumageRender::Setter::settings.width << "\n" << PlumageRender::Setter::settings.height << "\n" << 255 << "\n"; + + // ppm binary pixel data + for (uint32_t y = 0; y < PlumageRender::Setter::settings.height; y++) + { + unsigned int* row = (unsigned int*)data; + for (uint32_t x = 0; x < PlumageRender::Setter::settings.width; x++) + { + if (colorSwizzle) + { + file.write((char*)row + 2, 1); + file.write((char*)row + 1, 1); + file.write((char*)row, 1); + } + else + { + file.write((char*)row, 3); + } + row++; + } + data += subResourceLayout.rowPitch; + } + file.close(); + } + + + std::cout << "Screenshot saved to " << filePath << std::endl; + + // Clean up resources + vkUnmapMemory(VulkanBackend::VulkanFoundation::device, dstImageMemory); + vkFreeMemory(VulkanBackend::VulkanFoundation::device, dstImageMemory, nullptr); + vkDestroyImage(VulkanBackend::VulkanFoundation::device, dstImage, nullptr); + + screenshotSaved = true; } + +void PlumageRender::RenderOutput::outputImageSequence() +{ + // 比较已保存的帧数和设置里的开始帧数,在生成前清理上一次生成的图片序列 + if (savedFrameCounter == PlumageRender::Setter::settings.startFrameCount) + { + std::cout << "clean up directory for image sequence generation" << std::endl; + removeImageSequence(); + } + // 根据显卡编号设置输出路径(todo:提前到配置里) + PlumageRender::Setter::filePath.deviceSpecFilePath = PlumageRender::Setter::filePath.imageOutputPath + "/device" + std::to_string(PlumageRender::Setter::settings.selectedPhysicalDeviceIndex); + // 非第一次生成,生成结束的边界条件 + if (savedFrameCounter > PlumageRender::Setter::settings.endFrameIndex) + { + // 避免重复改变为true带来的无效性能开销 + if (PlumageRender::RenderOutput::signal.imageSequenceOutputComplete) + { + return; + } + // 生成结束的信号标志置为true + PlumageRender::RenderOutput::signal.imageSequenceOutputComplete = true; + // 构造ffmpeg脚本需要的路径变量(提前到配置) + std::string fileName = "/%dresult.ppm"; + PlumageRender::Setter::filePath.totalImageOutputPath = PlumageRender::Setter::filePath.deviceSpecFilePath + fileName; + return; + } + // 路径存在性检查,不存在则创建 + if (!std::filesystem::exists(PlumageRender::Setter::filePath.deviceSpecFilePath.c_str())) + { + std::filesystem::create_directories(PlumageRender::Setter::filePath.deviceSpecFilePath.c_str()); + } + // 拼接图片序列编号到路径里 + std::string fileName = "/" + std::to_string(savedFrameCounter) + "result.ppm"; + PlumageRender::Setter::filePath.totalImageOutputPath = PlumageRender::Setter::filePath.deviceSpecFilePath + fileName; + //std::cout << outputPath << std::endl; + // 写入文件 + writeImageToFile(PlumageRender::Setter::filePath.totalImageOutputPath.c_str()); + // 写入一帧后已保存帧数+1 + savedFrameCounter++; +} + +void PlumageRender::RenderOutput::imageSequenceToVideo() +{ + // 边界条件,图片序列输出未完成 + if (!PlumageRender::RenderOutput::signal.imageSequenceOutputComplete) + { + return; + } + // 边界条件,图片序列到视频的输出已完成 + if (PlumageRender::RenderOutput::signal.imageSequenceToVideoComplete) + { + return; + } + // 拼接视频保存的设备编号路径(提前到配置文件进行) + std::string deviceFilePath = PlumageRender::Setter::filePath.videoOutputPath + "/device" + std::to_string(PlumageRender::Setter::settings.selectedPhysicalDeviceIndex); + // 判断路径是否存在,不存在则创建 + if (std::filesystem::exists(deviceFilePath.c_str())) + { + std::filesystem::create_directories(deviceFilePath.c_str()); + } + // 构造结果视频路径 + std::string resultVideoPath = deviceFilePath + "/result.mp4"; + // 构造脚本需要参数,图片序列路径和设定的帧率 + std::string commandLineImageSequencePath = PlumageRender::Setter::filePath.totalImageOutputPath; + //std::string commandLineCodecAndResultPath = resultVideoPath; + std::string commandLineFrameRate = std::to_string(PlumageRender::Setter::settings.videoFrameRate); + // 根据不同系统使用不同脚本 +#if defined(_WIN32) + std::string commandLine = PlumageRender::Setter::filePath.image2videoBatFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath; +#else + std::string commandLine = filePath.image2videoShFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath; +#endif + std::cout << commandLine << std::endl; + std::system(commandLine.c_str()); + // 视频输出完成,置标志为true + PlumageRender::RenderOutput::signal.imageSequenceToVideoComplete = true; + std::cout << "vidoe codec complete,saved in:" << resultVideoPath << std::endl; + std::cout << "star to clean up image sequence" << std::endl; + removeImageSequence(); +} + +void PlumageRender::RenderOutput::removeImageSequence() +{ + // 函数非第一次运行的边界条件 + if (savedFrameCounter != PlumageRender::Setter::settings.startFrameCount) + { + // 检查视频输出完成的标志位 + if (!PlumageRender::RenderOutput::signal.imageSequenceToVideoComplete) + { + return; + } + + } + // 遍历删除图片序列文件和空文件夹 + if (std::filesystem::exists(PlumageRender::Setter::filePath.deviceSpecFilePath)) + { + for (const auto& entry : std::filesystem::directory_iterator(PlumageRender::Setter::filePath.deviceSpecFilePath)) + { + if (std::filesystem::is_directory(entry.path())) + { + std::filesystem::remove_all(entry.path()); + } + else + { + std::filesystem::remove(entry.path()); + } + } + std::filesystem::remove(PlumageRender::Setter::filePath.deviceSpecFilePath); + std::cout << "clean up complete" << std::endl; + } + return; + +} + diff --git a/src/render/renderIO.h b/src/render/renderIO.h index 62260c0..a5a1882 100644 --- a/src/render/renderIO.h +++ b/src/render/renderIO.h @@ -13,26 +13,24 @@ namespace PlumageRender RenderInput(); ~RenderInput(); - struct Signal - { - bool imageSequenceOutputComplete = false; - bool imageSequenceToVideoComplete = false; - }; - static Signal signal; + std::map environments; std::string selectedEnvironment = "papermill"; std::map scenes; std::string selectedScene = "DamagedHelmet"; - void loadScene(); + void loadScene(std::string fileName); void loadEnvironment(std::string fileName); + static void loadAssets(); + + static int32_t animationIndex; private: - int32_t animationIndex = 0; + float animationTimer = 0.0f; @@ -54,6 +52,23 @@ namespace PlumageRender RenderOutput(); ~RenderOutput(); + struct Signal + { + bool imageSequenceOutputComplete = false; + bool imageSequenceToVideoComplete = false; + }; + static Signal signal; + + static uint64_t savedFrameCounter; + + static void writeImageToFile(std::string filePath); + + static void outputImageSequence(); + + static void imageSequenceToVideo(); + + static void removeImageSequence(); + private: }; diff --git a/src/render/renderSetter.h b/src/render/renderSetter.h index bc8b29b..02f6d6e 100644 --- a/src/render/renderSetter.h +++ b/src/render/renderSetter.h @@ -41,8 +41,8 @@ namespace PlumageRender uint32_t selectedPhysicalDeviceIndex = 0; VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 多重采样倍率 - }settings; - + }; + static Settings settings; struct FilePath { @@ -100,8 +100,8 @@ namespace PlumageRender std::string image2videoBatFilePath = getAssetPath() + "script/image2video.bat"; std::string image2videoShFilePath = getAssetPath() + "script/image2video.sh"; - }filePath; - + }; + static FilePath filePath; private: diff --git a/src/render/renderUI.cpp b/src/render/renderUI.cpp index 4ce8c78..e5d569e 100644 --- a/src/render/renderUI.cpp +++ b/src/render/renderUI.cpp @@ -7,7 +7,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() ImGuiIO& io = ImGui::GetIO(); ImVec2 lastDisplaySize = io.DisplaySize; - io.DisplaySize = ImVec2((float)PlumageRender::renderMain::settings.width, (float)PlumageRender::renderMain::settings.height); + io.DisplaySize = ImVec2((float)PlumageRender::Setter::settings.width, (float)PlumageRender::Setter::settings.height); io.DeltaTime = frameTimer; io.MousePos = ImVec2(mousePos.x, mousePos.y); @@ -57,12 +57,12 @@ void PlumageRender::PlumageGUI::updateUIOverlay() vkDeviceWaitIdle(VulkanBackend::VulkanFoundation::device); std::wstring_convert> converter; std::string stringFilename = converter.to_bytes(filename); - loadScene(stringFilename); + PlumageRender::RenderInput::loadScene(stringFilename); PlumageRender::renderMain::vkFoundation.createDescriptorSets(); updateCBs = true; - PlumageRender::RenderInput::signal.imageSequenceOutputComplete = false; - PlumageRender::RenderInput::signal.imageSequenceToVideoComplete = false; - savedFrameCounter = 1; + PlumageRender::renderMain::renderOutput.signal.imageSequenceOutputComplete = false; + PlumageRender::renderMain::renderOutput.signal.imageSequenceToVideoComplete = false; + PlumageRender::renderMain::renderOutput.savedFrameCounter = 1; } } gui->endMenu(); @@ -71,22 +71,24 @@ void PlumageRender::PlumageGUI::updateUIOverlay() { if (gui->beginMenu(chineseUI.menuEnvironmentConfig)) { - if (gui->combo(chineseUI.environmentMap, PlumageRender::RenderInput::selectedEnvironment, PlumageRender::RenderInput::environments)) { - vkDeviceWaitIdle(VulkanBackend::VulkanFoundation::device); - loadEnvironment(environments[selectedEnvironment]); - setupDescriptors(); + auto& selectedEnvironment = PlumageRender::renderMain::renderInput.selectedEnvironment; + auto& environments = PlumageRender::renderMain::renderInput.environments; + if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) { + vkDeviceWaitIdle(PlumageRender::renderMain::vkFoundation.device); + PlumageRender::renderMain::renderInput.loadEnvironment(environments[selectedEnvironment]); + PlumageRender::renderMain::vkFoundation.createDescriptorSets(); updateCBs = true; } if (gui->checkbox(chineseUI.environmentBackGround, &PlumageRender::Setter::settings.displayBackground)) { updateShaderParams = true; } - if (gui->slider("Exposure", &VulkanBackend::VulkanFoundation::shaderData.exposure, 0.1f, 10.0f)) { + if (gui->slider("Exposure", &PlumageRender::renderMain::vkFoundation.shaderData.exposure, 0.1f, 10.0f)) { updateShaderParams = true; } - if (gui->slider("Gamma", &VulkanBackend::VulkanFoundation::shaderData.gamma, 0.1f, 4.0f)) { + if (gui->slider("Gamma", &PlumageRender::renderMain::vkFoundation.shaderData.gamma, 0.1f, 4.0f)) { updateShaderParams = true; } - if (gui->slider("IBL", &VulkanBackend::VulkanFoundation::shaderData.scaleIBLAmbient, 0.0f, 1.0f)) { + if (gui->slider("IBL", &PlumageRender::renderMain::vkFoundation.shaderData.scaleIBLAmbient, 0.0f, 1.0f)) { updateShaderParams = true; } gui->endMenu(); @@ -101,7 +103,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() "none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness" }; if (gui->combo(chineseUI.debugInput, &debugView.debugViewInputs, debugNamesInputs)) { - VulkanBackend::VulkanFoundation::shaderData.debugViewInputs = static_cast(debugView.debugViewInputs); + PlumageRender::renderMain::vkFoundation.shaderData.debugViewInputs = static_cast(debugView.debugViewInputs); updateShaderParams = true; } gui->endMenu(); @@ -112,7 +114,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() "none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular" }; if (gui->combo(chineseUI.debugPBREquation, &debugView.debugViewEquation, debugNamesEquation)) { - VulkanBackend::VulkanFoundation::shaderData.debugViewEquation = static_cast(debugView.debugViewEquation); + PlumageRender::renderMain::vkFoundation.shaderData.debugViewEquation = static_cast(debugView.debugViewEquation); updateShaderParams = true; } gui->endMenu(); @@ -127,20 +129,20 @@ void PlumageRender::PlumageGUI::updateUIOverlay() } if (gui->beginMenu(chineseUI.menuAnimation)) { - if (models.scene.animations.size() > 0) + if (PlumageRender::renderMain::models.scene.animations.size() > 0) { if (gui->beginMenu(chineseUI.menuAnimationActivation)) { - gui->checkbox(chineseUI.pauseAnimation, &animate); + gui->checkbox(chineseUI.pauseAnimation, &PlumageRender::Setter::settings.animate); gui->endMenu(); } if (gui->beginMenu(chineseUI.menuAnimationAnimationSequence)) { std::vector animationNames; - for (auto animation : models.scene.animations) { + for (auto animation : PlumageRender::renderMain::models.scene.animations) { animationNames.push_back(animation.name); } - gui->combo(chineseUI.animationSeq, &animationIndex, animationNames); + gui->combo(chineseUI.animationSeq, &PlumageRender::RenderInput::animationIndex, animationNames); gui->endMenu(); } } @@ -169,16 +171,16 @@ void PlumageRender::PlumageGUI::updateUIOverlay() bool updateBuffers = (gui->vertexBuffer.buffer == VK_NULL_HANDLE) || (gui->vertexBuffer.count != imDrawData->TotalVtxCount) || (gui->indexBuffer.buffer == VK_NULL_HANDLE) || (gui->indexBuffer.count != imDrawData->TotalIdxCount); if (updateBuffers) { - vkDeviceWaitIdle(device); + vkDeviceWaitIdle(VulkanBackend::VulkanFoundation::device); if (gui->vertexBuffer.buffer) { gui->vertexBuffer.destroy(); } - gui->vertexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize); + gui->vertexBuffer.create(PlumageRender::renderMain::vkFoundation.vulkanDevice, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize); gui->vertexBuffer.count = imDrawData->TotalVtxCount; if (gui->indexBuffer.buffer) { gui->indexBuffer.destroy(); } - gui->indexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indexBufferSize); + gui->indexBuffer.create(PlumageRender::renderMain::vkFoundation.vulkanDevice, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indexBufferSize); gui->indexBuffer.count = imDrawData->TotalIdxCount; } @@ -204,12 +206,12 @@ void PlumageRender::PlumageGUI::updateUIOverlay() } if (updateCBs) { - vkDeviceWaitIdle(device); - buildCommandBuffers(); - vkDeviceWaitIdle(device); + vkDeviceWaitIdle(PlumageRender::renderMain::vkFoundation.device); + PlumageRender::renderMain::vkFoundation.createCommandBuffer(); + vkDeviceWaitIdle(PlumageRender::renderMain::vkFoundation.device); } if (updateShaderParams) { - updateShaderData(); + PlumageRender::renderMain::vkFoundation.createCommandBuffer(); } } \ No newline at end of file diff --git a/src/render/vulkanFoundation.cpp b/src/render/vulkanFoundation.cpp index b35abc9..74674b0 100644 --- a/src/render/vulkanFoundation.cpp +++ b/src/render/vulkanFoundation.cpp @@ -616,14 +616,14 @@ void VulkanBackend::VulkanFoundation::createImageView() imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = colorAttachmentFormat; - imageCI.extent.width = PlumageRender::renderMain::settings.width; - imageCI.extent.height =PlumageRender::renderMain::settings.height; + imageCI.extent.width = PlumageRender::Setter::settings.width; + imageCI.extent.height =PlumageRender::Setter::settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples =PlumageRender::renderMain::settings.sampleCount; + imageCI.samples =PlumageRender::Setter::settings.sampleCount; imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -660,14 +660,14 @@ void VulkanBackend::VulkanFoundation::createImageView() // Depth target imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = depthFormat; - imageCI.extent.width =PlumageRender::renderMain::settings.width; - imageCI.extent.height =PlumageRender::renderMain::settings.height; + imageCI.extent.width =PlumageRender::Setter::settings.width; + imageCI.extent.height =PlumageRender::Setter::settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples =PlumageRender::renderMain::settings.sampleCount; + imageCI.samples =PlumageRender::Setter::settings.sampleCount; imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depthAttachment.image)); @@ -700,8 +700,8 @@ void VulkanBackend::VulkanFoundation::createImageView() imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = colorAttachmentFormat; - imageCI.extent.width =PlumageRender::renderMain::settings.width; - imageCI.extent.height =PlumageRender::renderMain::settings.height; + imageCI.extent.width =PlumageRender::Setter::settings.width; + imageCI.extent.height =PlumageRender::Setter::settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; @@ -749,8 +749,8 @@ void VulkanBackend::VulkanFoundation::createImageView() image.pNext = NULL; image.imageType = VK_IMAGE_TYPE_2D; image.format = depthFormat; - image.extent.width =PlumageRender::renderMain::settings.width; - image.extent.height =PlumageRender::renderMain::settings.height; + image.extent.width =PlumageRender::Setter::settings.width; + image.extent.height =PlumageRender::Setter::settings.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; @@ -852,7 +852,7 @@ void VulkanBackend::VulkanFoundation::createRenderPass() // Multisampled attachment that we render to attachments[0].format = colorAttachmentFormat; - attachments[0].samples =PlumageRender::renderMain::settings.sampleCount; + attachments[0].samples =PlumageRender::Setter::settings.sampleCount; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -873,7 +873,7 @@ void VulkanBackend::VulkanFoundation::createRenderPass() // Multisampled depth attachment we render to attachments[2].format = depthAttachmentFormat; - attachments[2].samples =PlumageRender::renderMain::settings.sampleCount; + attachments[2].samples =PlumageRender::Setter::settings.sampleCount; attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -1137,7 +1137,7 @@ void VulkanBackend::VulkanFoundation::createGraphicPipeline() multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; if (PlumageRender::Setter::settings.multiSampling) { - multisampleStateCI.rasterizationSamples =PlumageRender::renderMain::settings.sampleCount; + multisampleStateCI.rasterizationSamples =PlumageRender::Setter::settings.sampleCount; } std::vector dynamicStateEnables = { @@ -1158,7 +1158,7 @@ void VulkanBackend::VulkanFoundation::createGraphicPipeline() pipelineLayoutCI.setLayoutCount = static_cast(setLayouts.size()); pipelineLayoutCI.pSetLayouts = setLayouts.data(); VkPushConstantRange pushConstantRange{}; - pushConstantRange.size = sizeof(PlumageRender::renderMain::pbrmaterial.pushConstBlockMaterial); + pushConstantRange.size = sizeof(PBR::Material::pushConstBlockMaterial); pushConstantRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; pipelineLayoutCI.pushConstantRangeCount = 1; pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; @@ -1201,7 +1201,7 @@ void VulkanBackend::VulkanFoundation::createGraphicPipeline() pipelineCI.pStages = shaderStages.data(); if (PlumageRender::Setter::settings.multiSampling) { - multisampleStateCI.rasterizationSamples =PlumageRender::renderMain::settings.sampleCount; + multisampleStateCI.rasterizationSamples =PlumageRender::Setter::settings.sampleCount; } // Skybox pipeline (background cube) @@ -1264,8 +1264,8 @@ void VulkanBackend::VulkanFoundation::createFramebuffer() framebufferCreateInfo.renderPass = renderPass; framebufferCreateInfo.attachmentCount = 4; framebufferCreateInfo.pAttachments = attachments; - framebufferCreateInfo.width =PlumageRender::renderMain::settings.width; - framebufferCreateInfo.height =PlumageRender::renderMain::settings.height; + framebufferCreateInfo.width =PlumageRender::Setter::settings.width; + framebufferCreateInfo.height =PlumageRender::Setter::settings.height; framebufferCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); @@ -1284,8 +1284,8 @@ void VulkanBackend::VulkanFoundation::createFramebuffer() framebufferCreateInfo.renderPass = renderPass; framebufferCreateInfo.attachmentCount = 2; framebufferCreateInfo.pAttachments = attachments; - framebufferCreateInfo.width =PlumageRender::renderMain::settings.width; - framebufferCreateInfo.height =PlumageRender::renderMain::settings.height; + framebufferCreateInfo.width =PlumageRender::Setter::settings.width; + framebufferCreateInfo.height =PlumageRender::Setter::settings.height; framebufferCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); @@ -1323,8 +1323,8 @@ void VulkanBackend::VulkanFoundation::createSwapChainFramebuffer() frameBufferCI.renderPass = renderPass; frameBufferCI.attachmentCount = attachmentCount; frameBufferCI.pAttachments = attachments; - frameBufferCI.width =PlumageRender::renderMain::settings.width; - frameBufferCI.height =PlumageRender::renderMain::settings.height; + frameBufferCI.width =PlumageRender::Setter::settings.width; + frameBufferCI.height =PlumageRender::Setter::settings.height; frameBufferCI.layers = 1; @@ -1377,7 +1377,7 @@ void VulkanBackend::VulkanFoundation::createUniformBuffer() for (auto& uniformBuffer : uniformBuffers) { uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene)); uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox)); - uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData)); + uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(PBR::Material::shaderData)); } updateUniformBuffers(); } @@ -1500,21 +1500,21 @@ void VulkanBackend::VulkanFoundation::createSceneDescriptorSets() writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].scene; writeDescriptorSets[2].dstBinding = 2; - writeDescriptorSets[2].pImageInfo = &PlumageRender::renderMain::pbrmaterial.textures.irradianceCube.descriptor; + writeDescriptorSets[2].pImageInfo = &PBR::Material::textures.irradianceCube.descriptor; writeDescriptorSets[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[3].descriptorCount = 1; writeDescriptorSets[3].dstSet = descriptorSets[i].scene; writeDescriptorSets[3].dstBinding = 3; - writeDescriptorSets[3].pImageInfo = &PlumageRender::renderMain::pbrmaterial.textures.prefilteredCube.descriptor; + writeDescriptorSets[3].pImageInfo = &PBR::Material::textures.prefilteredCube.descriptor; writeDescriptorSets[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[4].descriptorCount = 1; writeDescriptorSets[4].dstSet = descriptorSets[i].scene; writeDescriptorSets[4].dstBinding = 4; - writeDescriptorSets[4].pImageInfo = &PlumageRender::renderMain::pbrmaterial.textures.lutBrdf.descriptor; + writeDescriptorSets[4].pImageInfo = &PBR::Material::textures.lutBrdf.descriptor; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); @@ -1536,11 +1536,11 @@ void VulkanBackend::VulkanFoundation::createMaterialDescriptorSets() std::vector imageDescriptors = { - PlumageRender::renderMain::pbrmaterial.textures.empty.descriptor, - PlumageRender::renderMain::pbrmaterial.textures.empty.descriptor, - material.normalTexture ? material.normalTexture->descriptor : PlumageRender::renderMain::pbrmaterial.textures.empty.descriptor, - material.occlusionTexture ? material.occlusionTexture->descriptor : PlumageRender::renderMain::pbrmaterial.textures.empty.descriptor, - material.emissiveTexture ? material.emissiveTexture->descriptor : PlumageRender::renderMain::pbrmaterial.textures.empty.descriptor + PBR::Material::textures.empty.descriptor, + PBR::Material::textures.empty.descriptor, + material.normalTexture ? material.normalTexture->descriptor : PBR::Material::textures.empty.descriptor, + material.occlusionTexture ? material.occlusionTexture->descriptor : PBR::Material::textures.empty.descriptor, + material.emissiveTexture ? material.emissiveTexture->descriptor : PBR::Material::textures.empty.descriptor }; if (material.pbrWorkflows.metallicRoughness) @@ -1651,7 +1651,7 @@ void VulkanBackend::VulkanFoundation::createSkyboxDescriptorSets() writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].skybox; writeDescriptorSets[2].dstBinding = 2; - writeDescriptorSets[2].pImageInfo = &PlumageRender::renderMain::pbrmaterial.textures.prefilteredCube.descriptor; + writeDescriptorSets[2].pImageInfo = &PBR::Material::textures.prefilteredCube.descriptor; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } @@ -1701,9 +1701,9 @@ void VulkanBackend::VulkanFoundation::createCommandBuffer() renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; - renderPassBeginInfo.renderArea.extent.width =PlumageRender::renderMain::settings.width; - renderPassBeginInfo.renderArea.extent.height =PlumageRender::renderMain::settings.height; - renderPassBeginInfo.clearValueCount =PlumageRender::renderMain::settings.multiSampling ? 3 : 2; + renderPassBeginInfo.renderArea.extent.width =PlumageRender::Setter::settings.width; + renderPassBeginInfo.renderArea.extent.height =PlumageRender::Setter::settings.height; + renderPassBeginInfo.clearValueCount =PlumageRender::Setter::settings.multiSampling ? 3 : 2; renderPassBeginInfo.pClearValues = clearValues; for (uint32_t i = 0; i < commandbuffers.size(); ++i) @@ -1723,7 +1723,7 @@ void VulkanBackend::VulkanFoundation::createCommandBuffer() vkCmdSetViewport(currentCB, 0, 1, &viewport); VkRect2D scissor{}; - scissor.extent = {PlumageRender::renderMain::settings.width,PlumageRender::renderMain::settings.height }; + scissor.extent = {PlumageRender::Setter::settings.width,PlumageRender::Setter::settings.height }; vkCmdSetScissor(currentCB, 0, 1, &scissor); VkDeviceSize offsets[1] = { 0 }; @@ -1874,6 +1874,15 @@ void VulkanBackend::VulkanFoundation::createFenceAndSemaphore() } } +void VulkanBackend::VulkanFoundation::updateShaderData() +{ + PBR::Material::shaderData.lightDir = glm::vec4( + sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), + sin(glm::radians(lightSource.rotation.y)), + cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), + 0.0f); +} + diff --git a/src/render/vulkanFoundation.h b/src/render/vulkanFoundation.h index 5b97244..baade36 100644 --- a/src/render/vulkanFoundation.h +++ b/src/render/vulkanFoundation.h @@ -22,7 +22,37 @@ namespace VulkanBackend { public: VulkanFoundation(); - ~VulkanFoundation(); + ~VulkanFoundation() + { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipeline(device, pipelines.pbr, nullptr); + vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr); + + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); + + models.scene.destroy(device); + models.skybox.destroy(device); + + for (auto buffer : uniformBuffers) { + buffer.params.destroy(); + buffer.scene.destroy(); + buffer.skybox.destroy(); + } + for (auto fence : waitFences) { + vkDestroyFence(device, fence, nullptr); + } + for (auto semaphore : renderCompleteSemaphores) { + vkDestroySemaphore(device, semaphore, nullptr); + } + for (auto semaphore : presentCompleteSemaphores) { + vkDestroySemaphore(device, semaphore, nullptr); + } + } const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" @@ -36,36 +66,31 @@ namespace VulkanBackend const int MAX_FRAME_IN_FLIGHT = 2; - const int frameRange =PlumageRender::renderMain::settings.endFrameIndex -PlumageRender::renderMain::settings.startFrameCount; + const int frameRange =PlumageRender::Setter::settings.endFrameIndex - PlumageRender::Setter::settings.startFrameCount; static VkDevice device; static vks::VulkanDevice* vulkanDevice; static VkQueue graphicQueue; - struct ShaderData { - glm::vec4 lightDir; - float exposure = 4.5f; - float gamma = 2.2f; - float prefilteredCubeMipLevels; - float scaleIBLAmbient = 1.0f; - float debugViewInputs = 0; - float debugViewEquation = 0; - }; - static ShaderData shaderData; + + static VkPipelineCache pipelineCache; + + static VkPhysicalDevice physicalDevice; + + static std::vector swapChainImages; + static VkFormat swapChainImageFormat; void initVulkan(); // 创建描述符集合 void createDescriptorSets(); + + void createCommandBuffer(); private: - - - - VkInstance instance; VkDebugUtilsMessengerEXT debugMessenger; @@ -92,7 +117,7 @@ namespace VulkanBackend std::vector presentModes; }; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + GLFWwindow* window; @@ -104,8 +129,7 @@ namespace VulkanBackend VkSwapchainKHR swapChain; - std::vector swapChainImages; - VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; std::vector swapChainImageViews; @@ -176,7 +200,7 @@ namespace VulkanBackend VkPipelineLayout pipelineLayout; - VkPipelineCache pipelineCache; + struct UniformBufferSet { Buffer scene; @@ -191,7 +215,10 @@ namespace VulkanBackend glm::vec3 camPos; } shaderDataScene, shaderDataSkybox; - + struct LightSource { + glm::vec3 color = glm::vec3(1.0f); + glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f); + } lightSource; std::vector uniformBuffers; @@ -277,12 +304,14 @@ namespace VulkanBackend // 创建命令缓存区 void allocateCommandBuffers(); - void createCommandBuffer(); + void createglTFNodeCommandBuffer(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode); // 创建栅栏和信号量,用于多帧并行的同步 void createFenceAndSemaphore(); + void updateShaderData(); + }; VulkanFoundation::VulkanFoundation()