原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/13-init_vertex_buffer.html
本章节代码文件是 13-init_vertex_buffer.cpp
vertex buffer 是 CPU 和 GPU 都可见的缓冲区,它包含描述你想要渲染的物体网格的顶点数据。一般来说,顶点数据包含位置数据(x,y,z)和可选颜色,法线,或其他信息。像其他 3D API一样,这里的方法是用顶点数据填充缓冲区,然后在绘制操作中传递给 GPU。
创建 vertex buffer 和创建 uniform buffer 几乎是一样的,就像你在 uniform 示例中做过的那样,从创建一个缓冲区对象开始。
VkBufferCreateInfo buf_info = {};
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buf_info.pNext = NULL;
buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
buf_info.size = sizeof(g_vb_solid_face_colors_Data);
buf_info.queueFamilyIndexCount = 0;
buf_info.pQueueFamilyIndices = NULL;
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buf_info.flags = 0;
res = vkCreateBuffer(info.device, &buf_info, NULL, &info.vertex_buffer.buf);
创建一个 uniform buffer 对象和一个 vertex buffer 对象唯一真正的不同就是 usage 字段的设置。
立方体的数据(g_vb_solid_face_colors_Data)包含了36个顶点,定义了12个三角形,6个立方体面上每个面2个。每个三角形还有关联着表面颜色。你可以检查 cube_data.h 文件来看看实际的数据。
这里也是,步骤和分配 uniform buffer 的步骤很相似。首先查询内存需求,包括考虑到机器限制比如内存对齐。查看示例中的代码就能看到这个过程和 uniform 示例中执行的步骤是很相似的。
分配完后,内存就被映射,用顶点数据初始化,再取消映射:
uint8_t *pData;
res = vkMapMemory(info.device, info.vertex_buffer.mem, 0,
mem_reqs.size, 0, (void **)&pData);
memcpy(pData, g_vb_solid_face_colors_Data,
sizeof(g_vb_solid_face_colors_Data));
vkUnmapMemory(info.device, info.vertex_buffer.mem);
作为最后一步,内存被绑定到缓冲区对象上:
res = vkBindBufferMemory(info.device, info.vertex_buffer.buf,
info.vertex_buffer.mem, 0);
示例按以下顺序排列顶点数据:
struct Vertex {
float posX, posY, posZ, posW; // Position data
float r, g, b, a; // Color
};
你需要创建顶点输入绑定来向GPU描述数据排列顺序。以下的 vi_binding 和 vi_attribs 成员在这里被设置,但是稍后在 subsequent 示例中才被使用作为创建图形管道的一部分。因为你正在看顶点数据格式,最好在这里就了解一下这些代码:
info.vi_binding.binding = 0;
info.vi_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
info.vi_binding.stride = sizeof(g_vb_solid_face_colors_Data[0]);
info.vi_attribs[0].binding = 0;
info.vi_attribs[0].location = 0;
info.vi_attribs[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
info.vi_attribs[0].offset = 0;
info.vi_attribs[1].binding = 0;
info.vi_attribs[1].location = 1;
info.vi_attribs[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
info.vi_attribs[1].offset = 16;
stride 是一个顶点的大小,或者说为获取下一个顶点需要给指针增加的数量。
binding 和 location 成员指向在 GLSL shader 源码里它们各自的值。你可以回顾 shader 示例中的着色器源码来了解对应关系。
即使第一个 attribute 是位置数据,也在attribute 0 的 format 成员中用一个4字节的 float 数据格式来描述它。attribute 1 的 format 更明显是一个颜色格式,因为 atttibute 1 就是颜色。
offset 成员就简单的表明在顶点数据中每个 attribute 在哪里被找到。
你可以先跳过设置 render pass 的大部分代码,因为你会在之后的示例中看到。但是现在,找到绑定 vertex buffer 和 render pass 的代码:
vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindVertexBuffers(info.cmd, 0, /* Start Binding */
1, /* Binding Count */
&info.vertex_buffer.buf, /* pBuffers */
offsets); /* pOffsets */
vkCmdEndRenderPass(info.cmd);
注意你只能在 render pass 内部将 vertex buffer 和 render pass进行连接;也就是说,在 vkCmdBeginRenderPass
和 vkCmdEndRenderPass
之间录入 command buffer 的时候。这实际上告诉GPU在绘制时使用什么 vertex buffer。