在之前的文章中已经讨论了很多帧缓冲区相关的东西,并且我们已经配置了渲染路径,渲染路径基于一个单独的与交换链图像具有相同格式的帧缓冲区,但我们实际上还没有创建它

在渲染路径创建时指定的附着通过封装为 VkFramebuffer 对象进行绑定。一个帧缓冲区对象引用了代表这些附着的所有 VkImageView 对象。当然我们这里只有一个,也就是颜色附着。然而,我们需要用于附着的图像取决于我们在展示时交换链提供的图像。这意味这我们必须对交换链中所有的图像都创建一个帧缓冲区,并且在渲染的时候使用与交换链提供的图像相对应的那个帧缓冲区

首先添加一个成员变量用来持有这些帧缓冲区

std::vector<VkFramebuffer> swapchainFramebuffers;

接下来添加一个成员函数来为这个数组创建对象,并在 initVulkan 中调用它

void initVulkan() {
    createInstance();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapchain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
    createFrambuffers();
}

void createFramebuffers() {

}

然后直接填充这个函数

void createFramebuffers() {
    swapchainFramebuffers.resize(swapchainImageViews.size());
    for (size_t i = 0; i < swapchainImageViews.size(); i++) {
        VkImageView attachments[] = {swapchainImageViews[i]};
        VkFramebufferCreateInfo framebufferInfo = {};
        framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
        framebufferInfo.renderPass = renderPass;
        framebufferInfo.attachmentCount = 1;
        framebufferInfo.pAttachments = attachments;
        framebufferInfo.width = swapchainExtent.width;
        framebufferInfo.height = swapchainExtent.height;
        framebufferInfo.layers = 1;

        if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchainFramebuffers[i]) != VK_SUCCESS)
            throw std::runtime_error("failed to create framebuffer!");
    }
}

首先第一步是调整数组的长度来持有所有的缓冲区

之后的创建过程十分显然。先通过创建信息的 renderPass 成员指定与帧缓冲区兼容的渲染路径。我们只能选择兼容的渲染路径,这大致上意味着它们都要使用相同数量和相同类型的附着

attachmentCountpAttachments 参数指定了帧缓冲区对应的图像视图对象,它需要与渲染路径创建信息中的 pAttachment 数组中的附着描述相匹配

widthheight 参数非常显然,layers 指的是图像的层数,我们使用的交换链图像都是单层的,因此层数为一

最后需要在 cleanup 中销毁所有的帧缓冲区

void cleanup() {
    for (auto framebuffer : swapchainFramebuffers)
        vkDestroyFramebuffer(device, framebuffer, nullptr);
    vkDestroyPipeline(device, graphicsPipeline, nullptr);
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
    vkDestroyRenderPass(device, renderPass, nullptr);
    for (auto imageView : swapchainImageViews)
        vkDestroyImageView(device, imageView, nullptr);
    vkDestroySwapchainKHR(device, swapchain, nullptr);
    vkDestroyDevice(device, nullptr);
    vkDestroySurfaceKHR(instance, surface, nullptr);
    vkDestroyInstance(instance, nullptr);

    glfwDestroyWindow(window);
    glfwTerminate();
}