Link

Habilitando o teste de profundidade

Outro problema que notamos é que partes da geometria parecem falhas. Isso acontece porque os fragmentos com profundidade maior (mais distantes) estão sendo renderizados sobre alguns fragmentos com profundidade menor, simplesmente porque aparecem depois no array de vértices.

Uma solução eficiente para esse problema é usar um buffer de profundidade, também conhecido como z-buffer. Um buffer de profundidade é um anexo adicional que armazena as profundidades dos fragmentos. A profundidade de cada fragmento produzido pelo rasterizador é comparada com o valor já presente nesse buffer. Se o fragmento estiver mais distante do que o já processado, é simplesmente eliminado. É possível manipular esse valor da mesma maneira que a cor.

O processo normal para habilitarmos esse recurso é criar um objeto de imagem para servir de buffer de profundidade (para isso, precisamos utilizar um formato de imagem de profundidade suportado pelo dispositivo); alocar memória do dispositivo para essa imagem; criar uma image view associada; vincular a imagem de profundidade ao anexo de profundidade do framebuffer; e, por fim, habilitar o teste de profundidade no pipeline gráfico.

A classe QWindow já cria uma imagem de profundidade e a vincula ao framebuffer. Só precisamos configurar o teste de profundidade no pipeline gráfico.

Isso é feito através da estrutura VkPipelineDepthStencilStateCreateInfo. Adicionamos essa nova estrutura antes da criação da variável pipelineInfo:

VkPipelineDepthStencilStateCreateInfo depthStencil = {};
depthStencil.sType =
	VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;

Queremos que o teste de profundidade esteja ativo, portando definimos depthTestEnable como VK_TRUE. Também queremos que fragmentos que passam no teste gravem sua própria profundidade no buffer de profundidade. Para isso, também, definimos depthWriteEnable como VK_TRUE. Isso é útil, por exemplo, no caso de objetos transparentes. Eles devem ser comparados a objetos opacos, mas não devem bloquear a renderização de outros objetos atrás deles, porque eles também participam da coloração. Para esse caso definiríamos esse campo como VK_FALSE.

depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;

O campo depthCompareOp é usado para fornecer o teste de comparação usado para preservar ou eliminar fragmentos. Usamos VK_COMPARE_OP_LESS (menor profundidade = mais perto) porque corresponde melhor à convenção usada pelo Vulkan.

depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.minDepthBounds = 0.0f;
depthStencil.maxDepthBounds = 1.0f;

Os campos depthBoundsTestEnable, minDepthBounds e maxDepthBounds são usados para testes opcionais de limite de profundidade. Isso permite manter apenas fragmentos cuja profundidade esteja entre os valores fornecidos em minDepthBounds e maxDepthBounds. Não usaremos esse recurso, portanto definimos depthBoundsTestEnable como VK_FALSE.

depthStencil.stencilTestEnable = VK_FALSE;
depthStencil.front = {};
depthStencil.back = {};

Os últimos três campos configuram as operações do buffer de estêncil, que também não usaremos neste projeto, portanto, definimos stencilTestEnable como VK_FALSE. Se quisermos usá-las, precisamos verificar se o formato selecionado para a imagem de profundidade também contém um componente para o estêncil.

Atualizamos a estrutura VkGraphicsPipelineCreateInfo para referenciar a estrutura que acabamos de preencher:

pipelineInfo.pDepthStencilState = &depthStencil;

Se executarmos nosso programa agora e carregar novamente o nosso modelo 3D pare ele, deverá ver que a geometria do modelo é renderizada corretamente (como mostrado na Figura 12).

Figura 12 – Modelo 3D com textura e teste de profundidade habilitado


Anterior Próximo