#ifndef TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION #endif #ifndef STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #endif #ifndef TINYGLTF_NO_STB_IMAGE_WRITE #define TINYGLTF_NO_STB_IMAGE_WRITE #endif #include "render.h" //#include "VulkanUtils.hpp" //#include "assetLoader.h" void PlumageRender::renderMain::initWindow(int Width, int Height) { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(Width, Height, "vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); } void PlumageRender::renderMain::framebufferResizeCallback(GLFWwindow* window, int width, int height) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } 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() { buildCommandBuffers(); vkDeviceWaitIdle(device); updateUniformBuffers(); //update UI updateUIOverlay(); } void PlumageRender::renderMain::prepare() { //VulkanExampleBase::prepare(); setupCamera(); loadAssets(); generateBRDFLUT(); generateCubemaps(); prepareUniformBuffers(); setupDescriptors(); preparePipelines(); if (!PlumageRender::Setter::settings.headless) { gui = new UI(vulkanDevice, renderPass, queue, pipelineCache,PlumageRender::renderMain::settings.sampleCount); updateUIOverlay(); } buildCommandBuffers(); prepared = true; } void PlumageRender::renderMain::setupCamera() { camera.type = Camera::CameraType::lookat; camera.setPerspective(45.0f, (float)PlumageRender::Setter::settings.width / (float)PlumageRender::Setter::settings.height, 0.1f, 256.0f); camera.rotationSpeed = 0.25f; camera.movementSpeed = 0.1f; camera.setPosition({ 0.0f, 0.0f, -1.0f }); camera.setRotation({ 0.0f, 0.0f, 0.0f }); } void PlumageRender::renderMain::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue) { VkSubmitInfo submitInfo = vks::initializers::submitInfo(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(); VkFence fence; VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX)); 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) { return; } if (!PlumageRender::Setter::settings.headless) { updateUIOverlay(); } //加入写到文件的函数 //swapChainImage = swapChain.images[frameIndex]; //outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath); outputImageSequence(); VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); imageSequenceToVideo(); VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer); if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) { windowResize(); } else { VK_CHECK_RESULT(acquire); } // Update UBOs updateUniformBuffers(); UniformBufferSet[currentUB] = uniformBuffers[currentBuffer]; memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene)); memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData)); memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox)); const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pWaitDstStageMask = &waitDstStageMask; submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; submitInfo.waitSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; submitInfo.signalSemaphoreCount = 1; submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; submitInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); //显示队列 VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { if (present == VK_ERROR_OUT_OF_DATE_KHR) { windowResize(); return; } else { VK_CHECK_RESULT(present); } } frameIndex += 1; frameIndex %= renderAhead; if (!paused) { if (PlumageRender::Setter::settings.rotateModel) { modelrot.y += frameTimer * 35.0f; if (modelrot.y > 360.0f) { modelrot.y -= 360.0f; } } if ((animate) && (models.scene.animations.size() > 0)) { animationTimer += frameTimer; if (animationTimer > models.scene.animations[animationIndex].end) { animationTimer -= models.scene.animations[animationIndex].end; } models.scene.updateAnimation(animationIndex, animationTimer); } updateShaderData(); if (PlumageRender::Setter::settings.rotateModel) { updateUniformBuffers(); } } if (camera.updated) { updateUniformBuffers(); } } void PlumageRender::renderMain::fileDropped(std::string filename) { vkDeviceWaitIdle(device); loadScene(filename); setupDescriptors(); buildCommandBuffers(); } // 重构到单独的UI里 void PlumageRender::renderMain::updateUIOverlay() { } // 完全重写,避免OS specific /* PlumageRender* plumageRender; #if defined(_WIN32) LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (plumageRender != NULL) { plumageRender->handleMessages(hWnd, uMsg, wParam, lParam); } return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) { VulkanBackend::Setter setter; for (int32_t i = 0; i < __argc; i++) {setter.args.push_back(__argv[i]); }; plumageRender = new PlumageRender(); plumageRender->initVulkan(); if (!plumageRender->PlumageRender::Setter::settings.headless) { plumageRender->setupWindow(hInstance, WndProc); } plumageRender->prepare(); plumageRender->renderLoop(); delete(plumageRender); return 0; } #endif */ int main() { PlumageRender::renderMain* plumageRender; PlumageRender::Setter setter; if (!PlumageRender::Setter::settings.headless) { plumageRender->initWindow(PlumageRender::Setter::settings.width,PlumageRender::renderMain::settings.height); } plumageRender->initVulkan(); return 0; }