Vulkan Tutorial 22 Index buffer
操作系统:Windows8.1
显卡:Nivida GTX965M
开发工具:Visual Studio 2017
Introduction
在实际产品的运行环境中3D模型的数据往往共享多个三角形之间的顶点数据。即使绘制一些简单的图形也是如此,比如矩形:

绘制矩形需要两个三角形,通常意味着我们需要6个顶点数据。问题是其中的两个顶点会重复,导致数据会有50%的冗余。如果更复杂的模型,该问题会更加严重,平均每三个三角形就会发生重复顶点使用的情况。解决问题的方法是使用index buffer,即索引缓冲区。
索引缓冲区纯粹是一个指向顶点缓冲区的指针数组。它允许我们重排列顶点数据,并复用多个已经存在的顶点数据。上图显示了有一个包含四个不重复顶点数据的顶点缓冲区,通过索引缓冲区将如何显示矩形的情况。前三个索引定义右上角的三角形,最后三个索引定义了左下角的三角形。
Index buffer creation
在本章节中,为了绘制如上图所示的矩形,我们需要修改顶点数据并添加索引数据。修改后的顶点数据分别代表矩形四个角:
const std::vector<Vertex> vertices = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
};
左上角是红色,右上角是绿色,右下角是蓝色,左下角是白色。我们添加一个新的索引数组indices代表索引缓冲区的数据内容。它应该匹配途中的索引来绘制右上角的三角形和左下角的三角形。
const std::vector<uint16_t> indices = {
, , , , ,
};
根据vertices中的条目个数,我们可以使用uint16_t或uint32_t作为索引缓冲区类型。现在我们可以使用uint16_t,因为我们使用的独立顶点数量小于65535。
如顶点数据,为了使GPU可以访问到它们,需要将索引数据上传到缓冲区VkBuffer。定义两个类成员保存索引缓冲区的资源:
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
createIndexBuffer函数与之前的createVertexBuffer函数非常类似:
void initVulkan() {
...
createVertexBuffer();
createIndexBuffer();
...
}
void createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(indices[]) * indices.size();
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(device, stagingBufferMemory, , bufferSize, , &data);
memcpy(data, indices.data(), (size_t) bufferSize);
vkUnmapMemory(device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
vkDestroyBuffer(device, stagingBuffer, nullptr);
vkFreeMemory(device, stagingBufferMemory, nullptr);
}
仅有的两个差异。bufferSize现在等于索引数量乘以索引类型的大小,该类型或者是uint16_t,或者是uint32_t。indexBuffer的用法需改用VK_BUFFER_USAGE_INDEX_BUFFER_BIT代替VK_BUFFER_USAGE_VERTEX_BUFFER_BIT。其他的过程是一致的。我们创建暂存缓冲区拷贝顶点数据的内容,并最终拷贝到设备本地索引缓冲区。
索引缓冲区在程序退出的时候需要清理,与顶点缓冲区类似:
void cleanup() {
cleanupSwapChain();
vkDestroyBuffer(device, indexBuffer, nullptr);
vkFreeMemory(device, indexBufferMemory, nullptr);
vkDestroyBuffer(device, vertexBuffer, nullptr);
vkFreeMemory(device, vertexBufferMemory, nullptr);
...
}
Using an index buffer
使用索引缓冲区绘制需要修改createCommandBuffers函数两个地方。首先需要绑定索引缓冲区,就像之前的顶点缓冲区一样。区别是现在仅使用一个索引缓冲区。不幸的是,不可能对每个顶点属性使用不同的索引,所以即使只有一个属性不同,我们仍然必须完全复制顶点数据。
vkCmdBindVertexBuffers(commandBuffers[i], , , vertexBuffers, offsets); vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, , VK_INDEX_TYPE_UINT16);
索引缓冲区使用vkCmdBindIndexBuffer绑定,它持有索引缓冲区作为参数,还需要偏移量和索引数据的类型。如前所述,可能的类型是VK_INDEX_TYPE_UINT16和VK_INDEX_TYPE_UINT32。
仅仅绑定索引缓冲区不会发生任何改变,我们还需要告知Vulkan在使用索引缓冲区后,对应的绘制命令的变化。移除vkCmdDraw函数,并用vkCmdDrawIndexed替换:
vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), , , , );
该函数的调用与vkCmdDraw非常类似。前两个参数指定索引的数量和几何instance数量。我们没有使用instancing,所以指定1。索引数表示被传递到顶点缓冲区中的顶点数量。下一个参数指定索引缓冲区的偏移量,使用1将会导致图形卡在第二个索引处开始读取。倒数第二个参数指定索引缓冲区中添加的索引的偏移。最后一个参数指定instancing偏移量,我们没有使用该特性。
现在运行程序如下所示:

现在我们已经通过索引缓冲区复用了顶点数据。在未来的加载复杂的3D模型数据中,该技术会非常的重要。
在前一章提到,为了更优的分配使用资源,推荐在单个内存中分配多个资源,如缓冲区,但是实际上,我们应该更进一步细化。来自Nvidia的驱动程序开发者建议将多个缓冲区(顶点缓冲区、索引缓冲区)存储到单个VkBuffer中。并在诸如vkCmdBindVertexBuffers之类的命令中使用偏移量。优点在于,在这种情况下,数据会更加充分的利用缓存,因为它们排列在一块区域。甚至在同一个渲染操作中可以复用来自相同内存块的多个资源块,只要刷新数据即可。该技巧称为称为aliasing,一些Vulkan函数有明确的标志指定这样做的意图。
项目代码 GitHub 地址。
Vulkan Tutorial 22 Index buffer的更多相关文章
- Vulkan Tutorial 20 Vertex buffer creation
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 在Vulkan中,缓冲区是内存的一块区域,该区域用于向显卡 ...
- Vulkan Tutorial 21 Staging buffer
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 顶点缓冲区现在已经可以正常工作,但相比于显卡内部读取数据, ...
- [译]Vulkan教程(22)创建顶点buffer
[译]Vulkan教程(22)创建顶点buffer Vertex buffer creation 创建顶点buffer Introduction 入门 Buffers in Vulkan are re ...
- [译]Vulkan教程(24)索引buffer
[译]Vulkan教程(24)索引buffer Index buffer 索引buffer Introduction 入门 The 3D meshes you'll be rendering in a ...
- directx12中vetex buffer、index buffer和constant buffer绑定piple line的时机
类别 时机 函数 建Heap vetex buffer 在Draw函数中 ID3D12GraphicsCommandList::IASetVertexBuffer 否 index buffer 在Dr ...
- Vulkan Tutorial 23 Descriptor layout and buffer
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...
- Vulkan Tutorial 29 Loading models
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 应用程序现在已经可以渲染纹理3D模型,但是 vertice ...
- Vulkan Tutorial 11 Shader modules
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 与之前的图像API不同,Vulkan中的着色器代码必须以二进制字节码的格式使用,而不 ...
- Vulkan Tutorial 12 Fixed functions
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 早起的图形API在图形渲染管线的许多阶段提供了默认的状态.在Vulkan中,从vie ...
随机推荐
- Flume总结(1)
一.日志采集:从网络端口接收数据,下沉到logger 文件netcat-logger.conf: # Name the components on this agent #给那三个组件取个名字 a1. ...
- java内存模型6-final
与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象 ...
- 华为ensp模拟某公司网络架构及配置详解
1.先晒下架构图,二层设备省略..... 2.下面开始具体配置详解 2.1.从路由器开始配置,先用远程工具远程AR1220F-S路由,secureCRT ,putty,xshell任选其中一个均可,功 ...
- 手机端的viewport属性
Window.devicePixelRatioThis read-only property returns the ratio of the resolution in physical pixel ...
- struts2.1.6教程二、struts.xml配置及例程
1.配置文件的优先级 在struts2中一些配置(比如常量)可以同时在struts-default.xml(只读性),strtus-plguin.xml(只读性),struts.xml,struts. ...
- 开涛spring3(7.4) - 对JDBC的支持 之 7.4 Spring提供的其它帮助
7.4 Spring提供的其它帮助 7.4.1 SimpleJdbc方式 Spring JDBC抽象框架提供SimpleJdbcInsert和SimpleJdbcCall类,这两个类通过利用JDB ...
- mongodb 创建LBS位置索引
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver< ...
- 更换包管理工具npm为yarn
官网:https://yarnpkg.com/zh-Hans/ 主要考虑: 1. npm管理安装模块依赖的版本不太方便,容易在删除node_modules重新install或在其他机器上新安装时, 安 ...
- Java之进程与线程
一.进程 二.线程 1.定义及特点 1)[定义]线程是一个进程内部的一条执行路径,Java虚拟机允许应用程序并发地运行多个执行路径 是系统独立调度和分派[CPU]的基本单位 2)特点 进程中执行运算的 ...
- HTMLCollection 对象详解,以及为什么循环获取的dom合集操作可能会出现下标不正确的情况?
有时候循环dom合集,然后操作其中的某些dom之后,发现下标不正确了 比如我们要删除一个dom合集的时候: var selectDom = document.getElementsByClassNam ...