[Vulkan教程]绘制一个三角形/图形管线基础/汇总(Conclusion)

本文介绍了如何在Vulkan中整合着色器、固定功能状态、管线布局和渲染通道,创建完整的图形管线,以实现3D渲染。重点讲解了VkGraphicsPipelineCreateInfo结构体的配置和各个组件的作用,为后续绘制三角形和帧缓冲做准备。

现在,我们可以结合前几章中所有结构体和对象来创建图形管线。它们是:

  • 着色器阶段:定义图像管线可编程阶段的着色器模块
  • 固定功能状态:定义管线固定功能阶段的结构体,包括输入组装、光栅化、视口和颜色混合
  • 管线布局:着色器引用的可动态更新的uniformpush values
  • 渲染通道:管线阶段引用的附件和其用法

这些结合起来就可以完整定义图形管线的功能了,现在我们在createGraphicsPipeline函数末尾填充VkGraphicsPipelineCreateInfo结构体。记得要在vkDestroyShaderModule之前完成创建。

我们首先引用VkPipelineShaderStageCreateInfo结构体:

VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;

然后是固定功能状态:

pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr; // Optional
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr; // Optional

然后是管线布局,注意这里是个句柄:

pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;

最后,引用渲染通道和该图形管线要用的子通道的下标。该管线也可以使用其它渲染通道,但它们必须与renderPass兼容。兼容性描述在这里,我们不会在本教程中使用。

pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;

还有两个参数basePipelineHandlebasePipelineIndex。Vulkan允许你从现有管线派生来创建新的图形管线。派生的目的是降低设置管线的成本,并且在拥有相同父亲的管线之间切换可以拥有更好的性能。我们可以通过basePipelineHandle指定现有管线,或通过basePipelineIndex引用一个即将通过索引创建的管线。我们现在只有一个管线,所以这里设置为null和无效索引。要使用这两个值还要在VkGraphicsPipelineCreateInfoflags上设置VK_PIPELINE_CREATE_DERIVATIVE_BIT

pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipelineInfo.basePipelineIndex = -1; // Optional

接下来,创建一个成员变量来保存VkPipeline对象:

VkPipeline graphicsPipeline;

创建图形管线:

if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
    throw std::runtime_error("failed to create graphics pipeline!");
}

vkCreateGraphicsPipelines函数相比其它Vulkan的对象创建函数拥有更多的参数,其目的是为了同时创建多个管线对象。
其第二个参数可以传递一个VkPipelineCache对象,它可以让我们存储和重用管线创建相关的数据,甚至可以存储到文件以跨程序使用。它可以显著加快管线的创建。我们之后在管线缓存一章中在讨论它。

常见的绘图操作均需要图形管线,因此,它也应该在程序结束时销毁:

void cleanup() {
    vkDestroyPipeline(device, graphicsPipeline, nullptr);
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
    ...
}

现在,我们可以运行下程序,确保为了创建图形管线所做的这么多工作没有问题。我们已经非常接近能在屏幕上看到三角形了。接下来的几章,我们将从交换链图像创建真正的帧缓冲并准备绘制命令。

目录
上一节 绘制一个三角形/图形管线基础/渲染通道(Render passes)
下一节 绘制一个三角形/绘制/帧缓冲(Framebuffers)

Vulkan绘制一个简单的图形,例如一个三角形,涉及多个步骤。以下是基本流程: ### 创建窗口和 Vulkan 上下文 首先,需要创建一个窗口并初始化 Vulkan 上下文。这通常涉及到使用 GLFW 或 SDL 这样的库来创建窗口,并设置 Vulkan 实例和表面。 ### 创建 Swap Chain 接下来,创建 Swap Chain,它负责管理用于渲染的图像资源。Swap Chain 的创建通常依赖于 Vulkan 上下文。 ### 创建着色器模块 在 Vulkan 中,图形管线的可编程部分由顶点着色器和片元着色器定义。这些着色器需要编译为 SPIR-V 格式,并通过 `VkShaderModule` 对象加载到 Vulkan 中。 ```cpp VkShaderModuleCreateInfo shaderInfo = {}; shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shaderInfo.codeSize = vertexShader.size(); shaderInfo.pCode = reinterpret_cast<const uint32_t*>(vertexShader.data()); VkShaderModule vertexShaderModule; vkCreateShaderModule(device, &shaderInfo, nullptr, &vertexShaderModule); ``` ### 创建 Render Pass Render Pass 定义了渲染操作的基本结构,包括颜色附件、深度/模板附件等。 ### 创建图形管线 使用 `VkPipeline` 对象创建图形管线,这包括设置顶点输入状态、光栅化状态、混合状态等。 ```cpp VkGraphicsPipelineCreateInfo pipelineInfo = {}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pDepthStencilState = &depthStencil; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = pipelineLayout; pipelineInfo.renderPass = renderPass; pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; VkPipeline graphicsPipeline; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); ``` ### 创建 FrameBuffer FrameBuffer 是渲染目标的集合,每个 FrameBuffer 通常对应一个 Swap Chain 图像。 ### 创建 Command Buffer Command Buffer 用于记录渲染命令。需要创建多个 Command Buffer,并在渲染循环中使用它们。 ### 渲染循环 在渲染循环中,从 Swap Chain 获取图像,记录 Command Buffer,提交命令到队列,并最终呈现图像到屏幕。 ```cpp // 获取图像 VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); // 记录 Command Buffer vkBeginCommandBuffer(commandBuffer, &beginInfo); // 开始 Render Pass vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); // 绑定管线 vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); // 绘制命令 vkCmdDraw(commandBuffer, 3, 1, 0, 0); // 结束 Render Pass vkCmdEndRenderPass(commandBuffer); vkEndCommandBuffer(commandBuffer); // 提交命令 VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = &imageAvailableSemaphore; submitInfo.pWaitDstStageMask = &pipelineStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderFinishedSemaphore; vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); // 呈现图像 VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &renderFinishedSemaphore; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &swapChain; presentInfo.pImageIndices = &imageIndex; vkQueuePresentKHR(presentQueue, &presentInfo); ``` 以上步骤概括了在 Vulkan绘制一个简单图形的基本流程。每个步骤都需要详细的配置和错误检查,以确保渲染正确进行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值