Vulkan_基于查询池的遮挡剔除

26 篇文章 27 订阅

前面章节我们简单的使用了解过Vulkan_基于GPU的视锥体剔除和LODCPU中实现的视锥体剔除,本次我们主要来使用Vulkan的遮挡查询来实现视锥体内的遮挡剔除功能。
在这里插入图片描述

想要具体了解所有剔除原理的同学,可以研读知乎图形大佬的这篇文章剔除:从软件到硬件

一、vulkan查询

从Vulkan中读取冲统计数据的主要机制是依靠查询对象。查询对象通过池进行创建和管理,每个对象实际上是池中的一个槽(slot),而不是一个单独管理的离散对象。针对用户提交给设备的业务,有多种不同的查询对象可供使用,每种用于测量设备上指令执行的一个方面。因为所有类型的查询对象都通过池来管理,所以查询的第一步是要创建一个查询池对象,用于保存这些查询对象。

1.1 遮挡查询

如果查询池的查询类型为VK_QUERY_TYPE_OCCLUSION,则统计通过深度和模板测试的片段数。这可以用于确定可见性,甚至可以以像素为单位测量几何体区域。
如果不太在意对象的可见区域,只关心对象是否可见,则在创建查询池的时候不在参数flags中设置VK_QUERY_CONTROL_PRECISE_BIT标识。如果未设置此标识,则查询结果可视为布尔值(即0不可见,非零可见)。

二、主要实现

2.1 创建查询池

我们新建一个setupQueryPool函数来创建用于存储遮挡查询结果的查询池,如下:

	// 查询池
	VkQueryPool queryPool;

	// 片段数
	uint64_t passedSamples[2] = { 1,1 };

	void setupQueryPool()
	{
		VkQueryPoolCreateInfo queryPoolInfo = {};
		queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
		queryPoolInfo.queryType = VK_QUERY_TYPE_OCCLUSION;
		queryPoolInfo.queryCount = 2;
		vkCreateQueryPool(device, &queryPoolInfo, NULL, &queryPool);
	}

之后我们创建一个函数getQueryResults来检索提交到命令缓冲区的阻塞查询的结果:

	void getQueryResults()
	{
		vkGetQueryPoolResults(
			device,
			queryPool,
			0,
			2,
			sizeof(passedSamples),
			passedSamples,
			sizeof(uint64_t),
			VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
	}

上边的获取函数中我们使用vkGetQueryResults将结果复制到主机可见的缓冲区中并且将结果存储为64位值,并等待结果完成。如果不想等待,可以使用VK_QUERY_RESULT_WITH_AVAILABILITY_BIT立刻返回结果的状态。

2.2 执行查询

查询通过在命令缓冲区中包含特定的开始和停止指令:vkCmdBeginQuery()和vkCmdEndQuery()来执行。
因此我们在buildCommandBuffers函数中来执行查询操作:

	void buildCommandBuffers()
	{
		...
		for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
		{
			...

			// 首先必须在渲染通道之前重置查询池
			vkCmdResetQueryPool(drawCmdBuffers[i], queryPool, 0, 2);

			...
			// 查询阶段
			// 茶壶绘制查询
			vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 0, VK_FLAGS_NONE);

			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
			//结束查询
			vkCmdEndQuery(drawCmdBuffers[i], queryPool, 0);

			// 圆球绘制开始查询
			vkCmdBeginQuery(drawCmdBuffers[i], queryPool, 1, VK_FLAGS_NONE);

			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
			//结束查询
			vkCmdEndQuery(drawCmdBuffers[i], queryPool, 1);

			// Visible pass
			// Clear color and depth attachments
			VkClearAttachment clearAttachments[2] = {};

			clearAttachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			clearAttachments[0].clearValue.color = defaultClearColor;
			clearAttachments[0].colorAttachment = 0;

			clearAttachments[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
			clearAttachments[1].clearValue.depthStencil = { 1.0f, 0 };

			VkClearRect clearRect = {};
			clearRect.layerCount = 1;
			clearRect.rect.offset = { 0, 0 };
			clearRect.rect.extent = { width, height };

			vkCmdClearAttachments(
				drawCmdBuffers[i],
				2,
				clearAttachments,
				1,
				&clearRect);

			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);

			// 是否绘制茶壶
			if (passedSamples[0] > 0)
			{
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.teapot, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.teapot.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.teapot.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.teapot.indexCount, 1, 0, 0, 0);
			}
			// 着色可见阶段
			// 是否绘制茶壶球体
			if (passedSamples[1] > 0)
			{
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets.sphere, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.sphere.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.sphere.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.sphere.indexCount, 1, 0, 0, 0);
			}
			// 遮挡面绘制
			vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.occluder);
			vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
			vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.plane.vertices.buffer, offsets);
			vkCmdBindIndexBuffer(drawCmdBuffers[i], models.plane.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(drawCmdBuffers[i], models.plane.indexCount, 1, 0, 0, 0);

			vkCmdEndRenderPass(drawCmdBuffers[i]);

			vkEndCommandBuffer(drawCmdBuffers[i]);
		}
	}

2.3 更新查询结果

通过上述执行查询后我们需要在renderLoop中实时更新获取到的片段数:

	void draw()
	{
		updateUniformBuffers();
		VulkanExampleBase::prepareFrame();

		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
		VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

		// 读取查询结果,以便在下一帧中使用
		getQueryResults();

		VulkanExampleBase::submitFrame();
	}

至此整个查询过程结束,可以通过下图对下遮挡剔除效果:
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值