Hello Vulkan(二)| 图形渲染新技术Vulkan Drawing

上一期技术分享中,已经讲述了关于Vulkan的前世今生,以及和OpenGL相比的不同点也放出第一个Vulkan的sample工程以供大家学习使用。当然,如果在学习过程中有任何问题,可以随时留言给我们,也欢迎大家移步至我们刚上线的开发者社区中交流和分享。

这一期将会给大家带来关于Vulkan基础的图形绘制方面内容,如何使用Vulkan绘制一些基础图形,以及渲染流程又是什么样的?

Vulkan图形拓扑 – 图元装配描述

图元装配描述,即对输入的顶点,到底构成什么图形,比如直线、三角形等,进行设置。介绍下Vulkan支持的主要图形拓扑:

VK_PRIMITIVE_TOPOLOGY_POINT_LIST: 点集
VK_PRIMITIVE_TOPOLOGY_LINE_LIST: 线集
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: 连续线
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:三角形集合
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:连续三角形

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:呈扇形的三角形集合

Typedef enum VkPrimitiveTopology

{

VK_PRIMITIVE_TOPOLOGY_POINT_LIST

VK_PRIMITIVE_TOPOLOGY_LINE_LIST

VK_PRIMITIVE_TOPOLOGY_LINE_STRIP

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY

VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY

VK_PRIMITIVE_TOPOLOGY_PATCH_LIST

} VkPrimitiveTopology;

观察下对于VkPrimitiveTopology的类型定义,不难看出,其实和OpenGL类似,出入并不是很大。

顶点描述模块

还记得上一期最后放出来的Vulkan渲染管线Pipeline吗?不记得的话,我们再看一遍。

 

在每条Pipeline,需要确定本条管线接收怎样的模型顶点数据。我们以绘制一个正方体为例:

首先我们对正方体顶点进行从0-7编号,参考如下代码:

struct vertex

{

glm::vec3 position;

glm::vec3 normal;

glm::vec3 color;

glm::vec2 texCoord;

};

#endif

struct vertex VertexData[ ] =

{

// triangle 0-2-3:

// vertex #0:

{

{ -1., -1., -1. },

{ 0., 0., -1. },

{ 0., 0., 0. },

{ 1., 0. }

},

// vertex #2:

{

{ -1., 1., -1. },

{ 0., 0., -1. },

{ 0., 1., 0. },

{ 1., 1. }

},

// vertex #3:

{

{ 1., 1., -1. },

{ 0., 0., -1. },

{ 1., 1., 0. },

{ 0., 1. }

},

…

};

我们剖析下Sample里这段代码,对于vertex #0,第一行的{ -1., -1., -1. },是v0的position,也就是x坐标是-1,y坐标是-1,z坐标是-1。第二行的{ 0., 0., -1. },是表面normal向量,表明了指向正方体的背面。第三行的{ 0., 0., 0. },是颜色,表示颜色是黑色的。最后一行的{ 1., 0. },代表的则是纹理坐标,即S-T坐标,S等于1,T等于0。依次可以类推vertex #2和vertex#3,并且v0、v2、v3一起组成了三角形0-2-3,也就是组成正方体6个面的12个三角形之一,只需要告诉系统运用三角形的图型拓扑即可。这些数据会以流的形式按顺序进行排列,并告诉着色器shader,那么如何让shader也能知道我们在这里定义的vertex结构体里position、normal、color、texCoord的顺序的呢?这个后续会讲述,这里我们只需要知道顶点流的一个流程即可。

GPU装载

因为调用的是三角形的图元拓扑,所以Vulkan每次抓取单位是3。这就是绘制的过程,这些结构体数组的数据流会被送入Vulkan,如果我们把它们都装入CPU内存,

就会造成极大的浪费,所以我们装入GPU内存,那我们如何实现呢?

参考下面的方法:

InitMyVertexDataBuffer( sizeof(VertexData), OUT &MyVertexDataBuffer ); //create

FillDataBuffer( MyVertexDataBuffer, (void *) VertexData ); //fill

VkResult
InitMyVertexDataBuffer( IN VkDeviceSize size, OUT MyBuffer * pMyBuffer ) {

VkResult result;
result = InitDataBuffer( size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, pMyBuffer );

return result;

}

VkResult
InitDataBuffer( VkDeviceSize size, VkBufferUsageFlags usage, OUT MyBuffer * pMyBuffer ) {

VkResult result = VK_SUCCESS;

VkBufferCreateInfo vbci;

vbci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; vbci.pNext = nullptr;
vbci.flags = 0;
vbci.size = pMyBuffer->size = size;

vbci.usage = usage;
vbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vbci.queueFamilyIndexCount = 0;

vbci.pQueueFamilyIndices = (const uint32_t *)nullptr;

result = vkCreateBuffer ( LogicalDevice, IN &vbci, PALLOCATOR, OUT &pMyBuffer->buffer );

VkMemoryRequirements vmr;
vkGetBufferMemoryRequirements( LogicalDevice, IN pMyBuffer->buffer, OUT &vmr );

VkMemoryAllocateInfo vmai;

vmai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;

vmai.pNext = nullptr;
vmai.allocationSize = vmr.size;
vmai.memoryTypeIndex = FindMemoryThatIsHostVisible( );

VkDeviceMemory vdm;
result = vkAllocateMemory( LogicalDevice, IN &vmai, PALLOCATOR, OUT &vdm ); pMyBuffer->vdm = vdm;

result = vkBindBufferMemory( LogicalDevice, pMyBuffer->buffer, IN vdm, 0 );

return result;

}

绘制

以上这个过程就是创建databuffer的过程。这里我们要确保我们在shader中数据格式完全匹配我们在CPU里期初定义的:

调用下面代码进行顶点绑定,以及告诉Command Buffer绘制哪些顶点进行绘制:

VkBuffer buffers[1] = { MyVertexDataBuffer.buffer };

VkDeviceSize offsets[1] = { 0 };

vkCmdBindVertexBuffers( CommandBuffers[nextImageIndex], 0, 1, buffers, offsets );

vkCmdDrawIndexed( commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);

到这儿我们就绘制出一个带有不同颜色顶点的立方体:

加上纹理映射后的效果:

其实这一期关于Vulkan绘制的内容,大多是对之前放出的Sample工程的详细讲解,从顶点定义,到创建GPU内存,到绘制等步骤,这些确实是非常枯燥,但是对于想学习Vulkan后续教程是必不可缺的一部分。

关于Vulkan以及shader相关的实践内容,我们会在本系列后续内容中继续与大家分享。

 敬请期待~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 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
发出的红包

打赏作者

虹图AI开放平台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值