Vulkan 渲染管线

大家好,接下来将为大家介绍Vulkan 渲染管线。

我们将会开启有关图形管线的话题,通过对图形渲染管线的配置完成最后的三角形绘画。所谓图形管线就是将mesh使用到的vertices定点数据和贴图数据,转化为渲染targets像素的操作序列。简要的概述如下图所示:

Input assembler:收集最原始的顶点数据,并且还可以使用索引缓冲区复用这些数据元素,而不必复制冗余的顶点数据副本。

vertex shader:会应用在每一个顶点数据,通常应用变换操作,从而将顶点的位置坐标数据从模型空间转换为屏幕空间。在处理完毕之后会将数据继续在管线传递下去。

tessellation shader:曲面着色器允许基于某些规则进一步细分几何数据增加网格的质量。经常应用与墙面和楼梯表面,使附近看起来不是那么平坦。

geometry shader:集合着色器应用在每一个图元上,可用于增减图元,它与曲面着色器类似,但更加灵活。然而,它在今天的应用中并没有太多的应用情景,因为除了Intel的集成显卡外,大多数显卡性能支持都不理想。

光栅化阶段:将图元分解为片元。这些是填充在帧缓冲区上的像素元素。屏幕外区域的片元会被丢弃,顶点着色器输出的数据在传递到片元着色器的过程中会进行内插值,如图所示。除此之外,根据深度测试的结果也会对片元进行丢弃。

fragment shader:应用于每个片元,确定每个帧缓冲区中写入的片元数据的颜色和深度值。片元着色器可以使用顶点着色器的插值数据,贴图的UV坐标和光源法线数据。

混色操作阶段:会对不同片元的颜色进行混合,最终映射到帧缓冲区的同一个像素上。片元也可以简单的互相重写,叠加或者根据透明度混合。

 

绿色的阶段被称为固定流水线。这个阶段允许使用自定义的参数数值,但是它内部的工作逻辑是预制好的。

橙色的阶段被称为可编程阶段programmable,我们可以向GPU提交自己编写的代码执行具体的逻辑。比如在使用fragment shader的时候,可以实现任何从贴图到灯光的光线追踪。这些程序同时运行在多核GPU上处理许多对象,比如并行的顶点和片段程序。

如果之前使用过旧的API(OpenGL和Direct3D),那么将可以随意通过glBlendFuncOMSetBlendState调用更改管线设置。Vulkan中的图形管线几乎不可改变,因此如果需要更改着色器,绑定到不同的帧缓冲区或者更改混合函数,则必须从头创建管线。缺点是必须创建一些管线,这些管线代表在渲染操作中使用的不同的组合状态。但是由于所有管线的操作都是提前知道的,所以可以通过驱动程序更好的优化它。

一些可编程阶段是基于打算做什么而决定的。比如,如果只是简单的绘制几何图形,则可以禁用tessellation和geometry着色器。如果只对深度值感兴趣,则可以禁用fragment着色器,这对于生成shadow map生成很有用。

在下一个章节中,我们首先创建将三角形放在屏幕上所需要的两个可编程阶段:vertice shader和fragment shader。固定功能的设置包括blending mode, viewport, rasterization将在之后的章节中设置。在Vulkan中设置图形流水线的最后一部分内容涉及输入和输出帧缓冲区。

initVulkan中创建函数createGraphicsPipeline并在createImageViews后立即调用。我们在下一章节介绍函数功能。

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createGraphicsPipeline();
}

...

void createGraphicsPipeline() {

}

在结尾处分享一下Vulkan官方的图形管线结构图,如下:

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个简单的使用Vulkan API编写渲染管线的示例代码: ```c++ #include <vulkan/vulkan.h> #include <vector> #include <iostream> int main() { // 初始化Vulkan实例 VkInstance instance; VkInstanceCreateInfo instanceCreateInfo = {}; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; vkCreateInstance(&instanceCreateInfo, nullptr, &instance); // 获取Vulkan设备列表 uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); std::vector<VkPhysicalDevice> physicalDevices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data()); // 选择第一个物理设备 VkPhysicalDevice physicalDevice = physicalDevices[0]; // 获取设备属性 VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); // 创建设备 VkDevice device; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); // 获取队列族索引 uint32_t queueFamilyIndex = 0; VkQueueFamilyProperties queueFamilyProperties; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, &queueFamilyProperties); for (uint32_t i = 0; i < queueFamilyCount; i++) { if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { queueFamilyIndex = i; break; } } // 创建命令池 VkCommandPool commandPool; VkCommandPoolCreateInfo commandPoolCreateInfo = {}; commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool); // 创建渲染管线 VkShaderModule vertexShaderModule; VkShaderModuleCreateInfo vertexShaderCreateInfo = {}; // 加载顶点着色器GLSL代码 vertexShaderCreateInfo.codeSize = sizeof(vertexShaderCode); vertexShaderCreateInfo.pCode = vertexShaderCode; vkCreateShaderModule(device, &vertexShaderCreateInfo, nullptr, &vertexShaderModule); VkShaderModule fragmentShaderModule; VkShaderModuleCreateInfo fragmentShaderCreateInfo = {}; // 加载片段着色器GLSL代码 fragmentShaderCreateInfo.codeSize = sizeof(fragmentShaderCode); fragmentShaderCreateInfo.pCode = fragmentShaderCode; vkCreateShaderModule(device, &fragmentShaderCreateInfo, nullptr, &fragmentShaderModule); VkPipelineShaderStageCreateInfo shaderStages[2] = {}; shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[0].module = vertexShaderModule; shaderStages[0].pName = "main"; shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shaderStages[1].module = fragmentShaderModule; shaderStages[1].pName = "main"; VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = {}; vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo = {}; inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssemblyCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = WIDTH; viewport.height = HEIGHT; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = {WIDTH, HEIGHT}; VkPipelineViewportStateCreateInfo viewportCreateInfo = {}; viewportCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportCreateInfo.viewportCount = 1; viewportCreateInfo.pViewports = &viewport; viewportCreateInfo.scissorCount = 1; viewportCreateInfo.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizationCreateInfo = {}; rasterizationCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizationCreateInfo.polygonMode = VK_POLYGON_MODE_FILL; rasterizationCreateInfo.cullMode = VK_CULL_MODE_BACK_BIT; rasterizationCreateInfo.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizationCreateInfo.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo multisampleCreateInfo = {}; multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.colorWriteMask = 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 colorBlendCreateInfo = {}; colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlendCreateInfo.attachmentCount = 1; colorBlendCreateInfo.pAttachments = &colorBlendAttachment; VkPipelineLayout pipelineLayout; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {}; pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout); VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCreateInfo.stageCount = 2; pipelineCreateInfo.pStages = shaderStages; pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; pipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo; pipelineCreateInfo.pViewportState = &viewportCreateInfo; pipelineCreateInfo.pRasterizationState = &rasterizationCreateInfo; pipelineCreateInfo.pMultisampleState = &multisampleCreateInfo; pipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo; pipelineCreateInfo.layout = pipelineLayout; pipelineCreateInfo.renderPass = renderPass; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline); // 渲染 VkCommandBuffer commandBuffer; VkCommandBufferAllocateInfo commandBufferAllocateInfo = {}; commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBufferAllocateInfo.commandPool = commandPool; commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBufferAllocateInfo.commandBufferCount = 1; vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffer); VkCommandBufferBeginInfo commandBufferBeginInfo = {}; commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo); vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdDraw(commandBuffer, 3, 1, 0, 0); vkCmdEndRenderPass(commandBuffer); vkEndCommandBuffer(commandBuffer); // 释放资源 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyShaderModule(device, vertexShaderModule, nullptr); vkDestroyShaderModule(device, fragmentShaderModule, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyPipeline(device, pipeline, nullptr); vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); return 0; } ``` 需要注意的是,上述示例代码仅仅是一个简单的渲染管线,实际应用中还需要添加更多的细节和逻辑,例如纹理加载、深度测试、光照等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值