#include "pbrrenderer2.hpp" #include "rendertarget.hpp" pbrRenderer::pbrRenderer () : vkRenderer() { } pbrRenderer::~pbrRenderer() { if (prepared) destroy(); } void pbrRenderer::destroy() { prepared = false; vkDestroyPipeline(device->dev, pipelines.skybox, nullptr); vkDestroyPipeline(device->dev, pipelines.pbr, nullptr); vkDestroyPipeline(device->dev, pipelines.pbrAlphaBlend, nullptr); for (uint i=0; iaddDescriptorSetLayout( {//scene { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//matrices { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//params { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//irradiance { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//prefiltered cube { 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//lutBrdf }); shadingCtx->addDescriptorSetLayout( {//meshes { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//texture sampler { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//material ubo }); /*shadingCtx->addDescriptorSetLayout( {//meshes { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//vkvg texture });*/ shadingCtx->prepare(); } void pbrRenderer::loadRessources() { textures.environmentCube.loadFromFile("data/textures/papermill_hdr16f_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, device ); skybox.loadFromFile("data/models/Box/glTF-Embedded/Box.gltf", device); generateBRDFLUT(); generateCubemaps(); } void pbrRenderer::freeRessources() { skybox.destroy(); textures.environmentCube.destroy(); textures.irradianceCube.destroy(); textures.prefilteredCube.destroy(); textures.lutBrdf.destroy(); } void pbrRenderer::prepareDescriptors() { dsScene = shadingCtx->allocateDescriptorSet(0); shadingCtx->updateDescriptorSet (dsScene, { {0,0,sharedUBOs.matrices}, {0,1,sharedUBOs.params}, {0,2,&textures.irradianceCube}, {0,3,&textures.prefilteredCube}, {0,4,&textures.lutBrdf} }); } void pbrRenderer::preparePipeline() { VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineRasterizationStateCreateInfo rasterizationStateCI = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; 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 = {VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; colorBlendStateCI.attachmentCount = 1; colorBlendStateCI.pAttachments = &blendAttachmentState; VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = {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 = {VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; viewportStateCI.viewportCount = 1; viewportStateCI.scissorCount = 1; VkPipelineMultisampleStateCreateInfo multisampleStateCI = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; if (renderTarget->samples > VK_SAMPLE_COUNT_1_BIT) multisampleStateCI.rasterizationSamples = renderTarget->samples; std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicStateCI = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); // Pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutCI = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; pipelineLayoutCI.setLayoutCount = shadingCtx->layouts.size(); pipelineLayoutCI.pSetLayouts = shadingCtx->layouts.data(); VK_CHECK_RESULT(vkCreatePipelineLayout(device->dev, &pipelineLayoutCI, nullptr, &pipelineLayout)); // Vertex bindings an attributes std::vector vertexInputBinding = { {0, sizeof(vkglTF::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX}, {1, sizeof(vkglTF::InstanceData), VK_VERTEX_INPUT_RATE_INSTANCE}, }; std::vector vertexInputAttributes = { { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3 }, { 2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6 }, //instance { 3, 1, VK_FORMAT_R32_UINT, 0 }, //material index { 4, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 4}, //model matrix { 5, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 20}, { 6, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 36}, { 7, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 52}, { 8, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 68}, //color for selection }; VkPipelineVertexInputStateCreateInfo vertexInputStateCI = {VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; vertexInputStateCI.vertexBindingDescriptionCount = 2; vertexInputStateCI.pVertexBindingDescriptions = vertexInputBinding.data(); vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); // Pipelines std::array shaderStages; VkGraphicsPipelineCreateInfo pipelineCI = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; pipelineCI.layout = pipelineLayout; pipelineCI.renderPass = renderTarget->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 = static_cast(shaderStages.size()); pipelineCI.pStages = shaderStages.data(); // Skybox pipeline (background cube) rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT; vertexInputStateCI.vertexBindingDescriptionCount = 1; vertexInputStateCI.vertexAttributeDescriptionCount = 3; shaderStages = { device->loadShader("skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), device->loadShader("skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox)); for (auto shaderStage : shaderStages) vkDestroyShaderModule(device->dev, shaderStage.module, nullptr); // PBR pipeline rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; vertexInputStateCI.vertexBindingDescriptionCount = 2; vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); depthStencilStateCI.depthWriteEnable = VK_TRUE; depthStencilStateCI.depthTestEnable = VK_TRUE; shaderStages = { device->loadShader("pbr.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), device->loadShader("pbr.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbr)); depthStencilStateCI.depthWriteEnable = VK_FALSE; depthStencilStateCI.depthTestEnable = VK_FALSE; rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; blendAttachmentState.blendEnable = VK_TRUE; blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrAlphaBlend)); for (auto shaderStage : shaderStages) vkDestroyShaderModule(device->dev, shaderStage.module, nullptr); //create full screen quad pipeline to render vkvg texture /*rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT; rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; vertexInputStateCI.vertexBindingDescriptionCount = 0; vertexInputStateCI.vertexAttributeDescriptionCount = 0; vertexInputStateCI.pVertexBindingDescriptions = nullptr; vertexInputStateCI.pVertexAttributeDescriptions = nullptr; shaderStages = { device->loadShader("FullScreenQuad.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), device->loadShader("simpletexture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);*/ } void pbrRenderer::draw(VkCommandBuffer cmdBuff) { vkCmdBindDescriptorSets(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &dsScene, 0, NULL); vkCmdBindPipeline(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); skybox.buildCommandBuffer (cmdBuff, pipelineLayout); // Opaque primitives vkCmdBindPipeline(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.pbr); models[0].buildCommandBuffer (cmdBuff, pipelineLayout); } void pbrRenderer::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->dev, &imageCI, nullptr, &textures.lutBrdf.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device->dev, textures.lutBrdf.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device->dev, 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->dev, &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->dev, &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->dev, &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->dev, &framebufferCI, nullptr, &framebuffer)); // Desriptors VkDescriptorSetLayout descriptorsetlayout; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->dev, &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->dev, &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 = { device->loadShader("genbrdflut.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), device->loadShader("genbrdflut.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) }; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device->dev, 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 = device->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 = renderTarget->width; scissor.extent.height = renderTarget->height; 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); device->flushCommandBuffer(cmdBuf, device->queue); vkQueueWaitIdle(device->queue); vkDestroyPipeline(device->dev, pipeline, nullptr); vkDestroyPipelineLayout(device->dev, pipelinelayout, nullptr); vkDestroyRenderPass(device->dev, renderpass, nullptr); vkDestroyFramebuffer(device->dev, framebuffer, nullptr); vkDestroyDescriptorSetLayout(device->dev, 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 = device; 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; } void pbrRenderer::generateCubemaps() { enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 }; for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) { vke::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->dev, &imageCI, nullptr, &cubemap.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device->dev, cubemap.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &cubemap.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device->dev, 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->dev, &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->dev, &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->dev, &renderPassCI, nullptr, &renderpass)); struct Offscreen { VkImage image; VkImageView view; VkDeviceMemory memory; VkFramebuffer framebuffer; } offscreen; // 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->dev, &imageCI, nullptr, &offscreen.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device->dev, offscreen.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &offscreen.memory)); VK_CHECK_RESULT(vkBindImageMemory(device->dev, 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->dev, &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->dev, &framebufferCI, nullptr, &offscreen.framebuffer)); VkCommandBuffer layoutCmd = device->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); device->flushCommandBuffer(layoutCmd, device->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->dev, &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->dev, &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->dev, &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->dev, 1, &writeDescriptorSet, 0, nullptr); struct PushBlockIrradiance { glm::mat4 mvp; float deltaPhi = (2.0f * float(M_PI)) / 180.0f; float deltaTheta = (0.5f * float(M_PI)) / 64.0f; } pushBlockIrradiance; struct PushBlockPrefilterEnv { glm::mat4 mvp; float roughness; uint32_t numSamples = 32u; } pushBlockPrefilterEnv; // 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(PushBlockIrradiance); break; case PREFILTEREDENV: pushConstantRange.size = sizeof(PushBlockPrefilterEnv); 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->dev, &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(vkglTF::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] = device->loadShader("filtercube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); switch (target) { case IRRADIANCE: shaderStages[1] = device->loadShader("irradiancecube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); break; case PREFILTEREDENV: shaderStages[1] = device->loadShader("prefilterenvmap.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); break; }; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device->dev, shaderStage.module, nullptr); } // Render cubemap VkClearValue clearValues[1]; clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; VkRenderPassBeginInfo renderPassBeginInfo = {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 = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkViewport viewport = {0, 0, (float)dim, (float)dim, 0.0f, 1.0f}; VkRect2D scissor = {{}, {renderTarget->width, renderTarget->height}}; vkCmdSetViewport(cmdBuf, 0, 1, &viewport); vkCmdSetScissor(cmdBuf, 0, 1, &scissor); VkImageSubresourceRange subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, numMips,0 , 6}; // Change image layout for all cubemap faces to transfer destination { 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); } for (uint32_t m = 0; m < numMips; m++) { for (uint32_t f = 0; f < 6; f++) { viewport.width = static_cast(dim * std::pow(0.5f, m)); viewport.height = static_cast(dim * std::pow(0.5f, m)); vkCmdSetViewport(cmdBuf, 0, 1, &viewport); // Render scene from cube face's point of view vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); // Pass parameters for current pass using a push constant block switch (target) { case IRRADIANCE: pushBlockIrradiance.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(PushBlockIrradiance), &pushBlockIrradiance); break; case PREFILTEREDENV: pushBlockPrefilterEnv.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; pushBlockPrefilterEnv.roughness = (float)m / (float)(numMips - 1); vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockPrefilterEnv), &pushBlockPrefilterEnv); break; }; vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL); skybox.buildCommandBuffer(cmdBuf, pipelinelayout); vkCmdEndRenderPass(cmdBuf); { 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); } } } { 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); } device->flushCommandBuffer(cmdBuf, device->queue); vkDestroyRenderPass(device->dev, renderpass, nullptr); vkDestroyFramebuffer(device->dev, offscreen.framebuffer, nullptr); vkFreeMemory(device->dev, offscreen.memory, nullptr); vkDestroyImageView(device->dev, offscreen.view, nullptr); vkDestroyImage(device->dev, offscreen.image, nullptr); vkDestroyDescriptorPool(device->dev, descriptorpool, nullptr); vkDestroyDescriptorSetLayout(device->dev, descriptorsetlayout, nullptr); vkDestroyPipeline(device->dev, pipeline, nullptr); vkDestroyPipelineLayout(device->dev, pipelinelayout, nullptr); cubemap.descriptor.imageView = cubemap.view; cubemap.descriptor.sampler = cubemap.sampler; cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; cubemap.device = device; switch (target) { case IRRADIANCE: textures.irradianceCube = cubemap; break; case PREFILTEREDENV: textures.prefilteredCube = cubemap; //lightingParams.prefilteredCubeMipLevels = 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; } }