当前位置: 首页 > news >正文

Vulkan学习笔记4—图形管线基础

一、图形管线概述

  • 输入汇编器(Input assembler)从您指定的缓冲区收集原始顶点数据,并且还可以使用索引缓冲区来重复某些元素,而无需复制顶点数据本身。

  • 顶点着色器(Vertex shader)为每个顶点运行,通常应用变换将顶点位置从模型空间转换为屏幕空间。它还将每个顶点的数据传递到管线中。

  • 细分着色器(Tessellation)允许您根据某些规则细分几何图形以提高网格质量。这通常用于使附近的砖墙和楼梯等表面看起来不那么平坦。

  • 几何着色器(Geometry shader)在每个图元(三角形、直线、点)上运行,可以丢弃它或输出比输入更多的图元。这与细分着色器类似,但更加灵活。但是,它在当今的应用程序中使用不多,因为除了英特尔的集成 GPU 外,大多数图形卡的性能都不是很好。

  • 光栅化阶段(Rasterization)将图元离散化为片段。这些是它们在帧缓冲上填充的像素元素。落在屏幕外的任何片段都会被丢弃,并且顶点着色器输出的属性会在片段之间进行插值,如图所示。通常,由于深度测试,此处也会丢弃其他图元片段后面的片段。

  • 片段着色器(Fragment shader)为每个幸存的片段调用,并确定将片段写入哪个(哪些)帧缓冲区,以及使用哪个颜色和深度值。它可以使用来自顶点着色器的插值数据来做到这一点,其中可以包括纹理坐标和用于照明的法线等内容。

  • 颜色混合阶段(Color blending)应用操作来混合映射到帧缓冲区中同一像素的不同片段。片段可以简单地相互覆盖、相加或基于透明度进行混合。

  • 颜色为绿色的阶段称为固定功能阶段。这些阶段允许您使用参数调整其操作,但它们的工作方式是预定义的。

  • 橙色的阶段是可编程的,这意味着您可以将自己的代码上传到图形卡以精确应用您想要的操作。这使您可以使用片段着色器,例如,实现从纹理和照明到光线追踪器的任何功能。这些程序在许多 GPU 内核上同时运行,以并行处理许多对象,如顶点和片段。

  • Vulkan 中的图形管线几乎完全不可变,因此如果您想更改着色器、绑定不同的帧缓冲区或更改混合功能,则必须从头开始重新创建管线。优点:管线中执行的所有操作都是预先知道的,因此驱动程序可以更好地对其进行优化;缺点:必须创建许多代表您希望在渲染操作中使用的所有不同状态组合的管线。

 

二、着色器模块

Vulkan 着色器代码使用 SPIR-V 字节码格式,可以通过 D:\VulkanSDK\1.4.313.2\Bin\glslc.exe 把 GLSL编写的着色器编译为字节码。

确保VulkanSDK环境变量配置正确,编写脚本compile.bat,放到着色器目录。

glslc triangle.vert -o triangle_vert.spv
glslc triangle.frag -o triangle_frag.spv

每次修改完 traingle glsl 着色器,运行 compile.bat。

 

Vulkan NDC(标准化设备坐标)和 OpenGL有差异,Y轴坐标符号翻转了。

着色器模块只参与创建管线过程,管线创建完成后直接销毁。

 

三、图形管线初步配置

void HelloTriangle::createGraphicsPipeline() {// 1. 读取着色器SPIR-V二进制代码auto vertShaderCode = utils::readFile("assets/shaders/triangle_vert.spv");auto fragShaderCode = utils::readFile("assets/shaders/triangle_frag.spv");// 2. 创建着色器模块(封装SPIR-V代码,供管线使用)VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);// --------------------- 3. 配置着色器阶段 ---------------------// 顶点着色器阶段信息VkPipelineShaderStageCreateInfo vertShaderStageInfo{};vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;       // 指定为顶点着色器阶段vertShaderStageInfo.module = vertShaderModule;               // 关联顶点着色器模块vertShaderStageInfo.pName = "main";                          // 指定入口函数名// 片段着色器阶段信息VkPipelineShaderStageCreateInfo fragShaderStageInfo{};fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;     // 指定为片段着色器阶段fragShaderStageInfo.module = fragShaderModule;               // 关联片段着色器模块fragShaderStageInfo.pName = "main";                          // 指定入口函数名// 合并着色器阶段配置数组VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};// --------------------- 4. 顶点输入配置 ---------------------VkPipelineVertexInputStateCreateInfo vertexInputInfo{};vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;vertexInputInfo.vertexBindingDescriptionCount = 0;            // 无顶点绑定描述(示例未使用顶点数据)vertexInputInfo.vertexAttributeDescriptionCount = 0;          // 无顶点属性描述// --------------------- 5. 图元装配配置 ---------------------VkPipelineInputAssemblyStateCreateInfo inputAssembly{};inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // 图元拓扑为三角形列表inputAssembly.primitiveRestartEnable = VK_FALSE;              // 禁用图元重启// --------------------- 6. 视口与裁剪配置 ---------------------VkPipelineViewportStateCreateInfo viewportState{};viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;viewportState.viewportCount = 1;                              // 单个视口viewportState.scissorCount = 1;                               // 单个裁剪区域// --------------------- 7. 光栅化阶段配置 ---------------------VkPipelineRasterizationStateCreateInfo rasterizer{};rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;rasterizer.depthClampEnable = VK_FALSE;                       // 禁用深度值裁剪rasterizer.rasterizerDiscardEnable = VK_FALSE;                // 禁用光栅化丢弃rasterizer.polygonMode = VK_POLYGON_MODE_FILL;                // 多边形填充模式(实体填充)rasterizer.lineWidth = 1.0f;                                  // 线宽rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;                  // 背面剔除rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;               // 顺时针为正面rasterizer.depthBiasEnable = VK_FALSE;                        // 禁用深度偏移// --------------------- 8. 多重采样配置 ---------------------VkPipelineMultisampleStateCreateInfo multisampling{};multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;multisampling.sampleShadingEnable = VK_FALSE;                 // 禁用采样着色multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;   // 1倍采样(无抗锯齿)// --------------------- 9. 颜色混合配置 ---------------------VkPipelineColorBlendAttachmentState colorBlendAttachment{};colorBlendAttachment.colorWriteMask =                         // 允许写入RGBA通道VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;colorBlendAttachment.blendEnable = VK_FALSE;                   // 禁用颜色混合VkPipelineColorBlendStateCreateInfo colorBlending{};colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;colorBlending.logicOpEnable = VK_FALSE;                        // 禁用逻辑操作colorBlending.logicOp = VK_LOGIC_OP_COPY;                      // 逻辑操作类型(示例未使用)colorBlending.attachmentCount = 1;                             // 单个颜色附件colorBlending.pAttachments = &colorBlendAttachment;            // 关联颜色附件配置// --------------------- 10. 动态状态配置 ---------------------std::vector<VkDynamicState> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};VkPipelineDynamicStateCreateInfo dynamicState{};dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());dynamicState.pDynamicStates = dynamicStates.data();            // 动态视口和裁剪区域(绘制时动态设置)// --------------------- 11. 管线布局创建 ---------------------VkPipelineLayoutCreateInfo pipelineLayoutInfo{};pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;pipelineLayoutInfo.setLayoutCount = 0;                          // 无描述符集布局pipelineLayoutInfo.pushConstantRangeCount = 0;                  // 无推送常量范围// 创建管线布局(用于绑定着色器资源)if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {throw std::runtime_error("failed to create pipeline layout!");}// --------------------- 12. 资源清理 ---------------------// 管线创建完成后,着色器模块不再需要,销毁以释放资源vkDestroyShaderModule(device, fragShaderModule, nullptr);vkDestroyShaderModule(device, vertShaderModule, nullptr);
}

着色器中使用 uniform 值是类似于动态状态变量的全局变量,可以在绘制时更改以改变着色器的行为,而无需重新创建它们。uniform 值需要在创建管线期间通过创建 VkPipelineLayout 对象来指定。

推送常量是另一种将动态值传递给着色器的方法,也在管线布局中指定。

 

四、渲染过程

在创建管线前,需告知Vulkan渲染时使用的帧缓冲附件,包括颜色/深度缓冲区数量、采样数及内容处理方式。这些信息封装在渲染过程对象中,将通过createRenderPass函数实现。

void HelloTriangle::createRenderPass() {// 1. 配置颜色附件VkAttachmentDescription colorAttachment{};colorAttachment.format = swapChainImageFormat;              // 使用交换链图像格式colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;            // 无多重采样colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;       // 渲染前清除附件colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;     // 渲染后保存结果colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;  // 不使用模板缓冲colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;  // 初始布局未定义colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;  // 最终用于呈现// 2. 配置附件引用VkAttachmentReference colorAttachmentRef{};colorAttachmentRef.attachment = 0;                          // 引用索引0的附件colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;  // 用作颜色附件时的最佳布局// 3. 配置子过程VkSubpassDescription subpass{};subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;  // 图形管线子过程subpass.colorAttachmentCount = 1;                           // 1个颜色附件subpass.pColorAttachments = &colorAttachmentRef;            // 关联颜色附件引用// 4. 创建渲染过程VkRenderPassCreateInfo renderPassInfo{};renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;renderPassInfo.attachmentCount = 1;                         // 1个附件renderPassInfo.pAttachments = &colorAttachment;             // 关联附件描述renderPassInfo.subpassCount = 1;                            // 1个子过程renderPassInfo.pSubpasses = &subpass;                       // 关联子过程描述// 创建渲染过程对象if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {throw std::runtime_error("Failed to create render pass!");}
}

 

五、完善图形管线配置

已拥有的对象类型:

  • 着色器阶段:定义图形管线可编程阶段功能的着色器模块

  • 固定功能状态:定义管线固定功能阶段的所有结构,如输入汇编、光栅化器、视口和颜色混合

  • 管线布局:着色器引用的、可在绘制时更新的 uniform 和 push 值

  • 渲染通道:管线阶段引用的附件及其用法

void HelloTriangle::createGraphicsPipeline() {    // ...省略之前的着色器模块创建和状态配置代码// 配置图形管线创建信息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.pColorBlendState = &colorBlending;        // 颜色混合状态pipelineInfo.pDynamicState = &dynamicState;          // 动态状态配置pipelineInfo.layout = pipelineLayout;                // 关联管线布局pipelineInfo.renderPass = renderPass;                // 关联渲染过程pipelineInfo.subpass = 0;                           // 使用渲染过程的第0号子过程pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;    // 不基于其他管线创建//  创建图形管线if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {throw std::runtime_error("Failed to create graphics pipeline!");}// 之前的代码: 资源清理:管线创建后不再需要着色器模块vkDestroyShaderModule(device, fragShaderModule, nullptr);vkDestroyShaderModule(device, vertShaderModule, nullptr);
}

 

六、新增成员变量和成员函数

成员变量

 VkRenderPass renderPass;VkPipelineLayout pipelineLayout;VkPipeline graphicsPipeline;

 

成员函数

void createRenderPass();
void createGraphicsPipeline();
VkShaderModule createShaderModule(const std::vector<char>& code);

清理资源:

void HelloTriangle::cleanup() {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);if (enableValidationLayers) {DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);}vkDestroySurfaceKHR(instance, surface, nullptr);vkDestroyInstance(instance, nullptr);
}

 

当前代码分支:03_graphics-pipeline

 

 

相关文章:

  • ubuntu20.04 安装Mujoco 及 Isaac Gym 仿真器
  • 紫光展锐完成优化升级,支持Android 16,以科技创新共赴智能体验新篇章
  • 常见的测试工具及分类
  • 系统功耗管理
  • 从零搭建智能家居:香橙派+HomeAssistant实战指南
  • 【android bluetooth 框架分析 04】【bt-framework 层详解 6】【Properties介绍】
  • Springboot仿抖音app开发之消息业务模块后端复盘及相关业务知识总结
  • php反序列化漏洞学习
  • [安卓按键精灵辅助工具]一些安卓端可以用的雷电模拟器adb命令
  • 关于安卓dialogFragment中,EditText无法删除文字的问题
  • Android NTP自动同步时间机制
  • 展开说说Android之Glide详解_使用篇
  • DRG支付场景模拟器扩展分析:技术实现与应用价值
  • 算法导论第三章:数据结构艺术与高效实现
  • 为什么TCP有粘包问题,而UDP没有
  • 前端导出PDF(适配ios Safari浏览器)
  • 力扣HOT100之技巧:136. 只出现一次的数字
  • opencl的简单介绍以及c++实例
  • 爱普生FC-135R晶振在广域网LoRa设备中的应用
  • openEuler 虚拟机中 Shell 脚本实现自动化备份与清理实践
  • 香港公司注册代理/seo外包公司专家
  • 做招聘网站需要哪些手续/真正免费的网站建站
  • 上海跨境电商网站制作/地推公司
  • 网站从哪些方面来做/seo基础视频教程
  • 怎么做网站链接的快捷方式/南宁seo外包服务商
  • 做网站需要用到的语言/域名注册官网免费