Vulkan 学习(17)---- 使用 IndexBuffer

Index Buffer 简介

绘制一个矩形可以通过绘制两个三角形来实现,如果不共享顶点,就需要 6 个顶点,共享顶点的话,只需要 4 个顶点就可以
可以想象对于更加复杂的三维网格,通过共享顶点可以节约大量内存资源

indexBuffer

索引缓冲(IndexBuffer)是一个包含了指向顶点缓冲中顶点数据的索引数组的缓冲,使用索引缓冲,我们可以对顶点数据进行复用

创建 IndexBuffer

  1. 首先定义矩形的四个顶点
const std::vector<Vertex> vertices = {
    {{-0.5f, 0.5f}, {1.0f, 0.0f, 0.0f}}, // color R
    {{-0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, // color G
    {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, // color B
    {{0.5f, -0.5f }, {1.0f, 0.0f, 0.0f} } // color R
};

四个顶点定义两个三角形,这个两个三角形的绘制顺序如下,

// 这里按照顺时针的方式绘制
const std::vector<uint16_t> indices = {
    0, 1, 3, 0, 3, 2
};

index

可以使用 uint16_tuint32_t 变量类型作为索引的类型,这里是根据顶点的最大数量作为定义依据
方法还是将索引数据加载到一个VkBuffer来让 GPU 可以访问它,我们定义了两个类成员变量来存储索引缓冲对象

VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
std::vector<uint16_t> drawIndices;

添加 createIndexBuffer 函数用于索引缓冲创建,它和内容和 createVertexBuffer 函数的内容几乎一样

void basicTriangleIndexBuffer::createIndexBuffer() {
	VkDeviceSize bufferSize = sizeof(drawIndices[0]) * drawIndices.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, 0, bufferSize, 0, &data);
	memcpy(data, drawIndices.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 的大小是根据顶点的数量计算出来的
  • indexBuffer 的用法标记为 VK_BUFFER_USAGE_INDEX_BUFFER_BIT

除此之外的处理和顶点缓冲的创建是相同的, 我们也需要创建一个暂存缓冲来存储 indices 数组中的索引数据,然后复制暂存缓冲中的索引数据到 GPU 能够快速访问的缓冲中

使用 IndexBuffer 绘制

首先我们需要将顶点缓冲区绑定到指令缓存对象上,和绑定顶点缓冲基本类似,不同之处是我们只能绑定一个索引缓冲对象

vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16);

索引缓冲通过调用 vkCmdBindIndexBuffer函数来进行绑定,vkCmdBindIndexBuffer 函数以索引缓冲对象,索引数据在索引缓冲中的偏移,以及索引数据的类型作为参数

仅仅绑定索引缓冲是不会起任何作用的,我们需要使用 vkCmdDrawIndexed 指令替换之前使用 vkCmdDraw 指令进行绘制操作

vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(drawIndices.size()), 1, 0, 0, 0);
//替代
//vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertices.size()), 1, 0, 0);

vkCmdDrawIndexed 函数使用和 vkCmdDraw 函数类似,前两个参数分别为指令缓冲区对象(commandbuffer)和指定索引的个数,第三个参数表示实例的个数,在这里我们没有使用实例渲染,所以将实例个数设置为 1
第四个参数偏移值用于指定 GPU 开始读取索引的位置,偏移值为 1 对应索引数据中的第二个索引
倒数第二个参数是检索顶点数据前加到顶点索引上的数值
最后一个参数用于第一个被渲染的实例的ID,在这里我们没有使用

最后显示效果如下:
index_draw
这里我们分别创建了 vertexBuffer indexBuffer,实际上可以更进一步,使用一个缓冲对象通过偏移值来存储多个不同的顶点缓冲和索引缓冲数据
也就是只创建一个 vkBuffer 类型,这样做之后,由于数据之间非常紧凑,可以更好地被缓存,对于没有同时进行的操作使用的内存块可以供多个对象复用

This is known as aliasing and some Vulkan functions have explicit flags to specify that you want to do this

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值