Link

Criando uma image view

Em muitos casos, o recurso de imagem não pode ser usado diretamente, pois mais informações sobre ele são necessárias do que as incluídas no próprio recurso. Por exemplo, não podemos vincular uma imagem a um conjunto de descritores para obter uma amostra dela em um shader. Para satisfazer esses requisitos adicionais, devemos criar uma image view (visualização de imagem) representada no Vulkan por um objeto VkImageView. Uma image view é essencialmente uma coleção de propriedades e uma referência a um objeto VkImage.

Uma image view também permite que toda ou parte de uma imagem existente seja vista como um formato diferente. Por exemplo, podemos ter uma imagem multicamadas (array 2D) e queremos renderizar apenas para uma camada de array específica.

Adicionamos um membro de estrutura em Object3D para manter um objeto VkImageView para a imagem de textura e criamos uma nova função createTextureImageView onde criaremos esse objeto. Chamamos essa função no final de addTextureImage:

...
VkImage textureImage = VK_NULL_HANDLE;
VkDeviceMemory textureImageMemory = VK_NULL_HANDLE;
VkImageView textureImageView = VK_NULL_HANDLE;
...
void Renderer::createTextureImageView() {

}
..
void Renderer::addTextureImage(QString texturePath) {
	...
	createTextureImageView();
}

Para criar uma image view, precisamos preparar uma estrutura do tipo VkImageViewCreateInfo:

VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.pNext = nullptr;
viewInfo.flags = 0;
viewInfo.image = m_object->textureImage;

O campo flags dessa estrutura é reservado para uso futuro e deve ser definido como 0. O objeto VkImage do qual criaremos uma nova image view é especificado em image.

viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;

O tipo de visualização a ser criado é especificado em viewType. O tipo de visualização deve ser compatível com o tipo de imagem e é um membro da enumeração VkImageViewType, que é maior que a enumeração VkImageType usada na criação da imagem. Os tipos de exibição de imagem são os seguintes:

  • VK_IMAGE_VIEW_TYPE_1D, VK_IMAGE_VIEW_TYPE_2D e VK_IMAGE_VIEW_TYPE_3D são os tipos “normais” de imagem 1D, 2D e 3D, respectivamente.
  • VK_IMAGE_VIEW_TYPE_CUBE e VK_IMAGE_VIEW_TYPE_CUBE_ARRAY são imagens de mapa de cubo (cube map) e de array de mapa de cubo, respectivamente.
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;

O formato da nova visualização é especificado em format. Esse deve ser um formato compatível com o da imagem. Em geral, se dois formatos tiverem o mesmo número de bits por pixel, eles serão considerados compatíveis. Estamos utilizando aqui o mesmo formato que a imagem.

viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;

A ordem dos componentes na image view pode ser diferente daquela na imagem. Isso permite, por exemplo, criar uma visualização RGBA de uma imagem no formato BGRA. Esse remapeamento é especificado usando uma instância de VkComponentMapping incorporada em components. Cada membro de VkComponentMapping especifica a fonte de dados na imagem que será usada para preencher o texel resultante obtido da image view. Eles são membros da enumeração VkComponentSwizzle. No nosso caso, queremos que os dados na image view sejam lidos no canal correspondente na imagem. Para isso, usamos o sinalizador VK_COMPONENT_SWIZZLE_IDENTITY para cada componente.

viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;

O subconjunto da imagem que queremos visualizar é especificado no campo subresourceRange. Este é uma instância da estrutura VkImageSubresourceRange. O campo aspectMask dessa estrutura é um campo de bits composto por membros da enumeração VkImageAspectFlagBits, que especifica quais aspectos da imagem desejamos consultar o layout. Alguns tipos de imagens têm mais de uma parte lógica, mesmo que os dados em si possam ser intercalados ou relacionados de alguma forma. Um exemplo disso são as imagens de estêncil e profundidade, que possuem um componente de profundidade e um componente de estêncil. Cada um desses dois componentes pode ser visualizado como uma imagem separada por si só, e essas sub-imagens são conhecidas como aspectos. Os sinalizadores que podem ser incluídos no aspectMask são:

  • VK_IMAGE_ASPECT_COLOR_BIT: A parte colorida de uma imagem. Geralmente, há apenas um aspecto colorido nas imagens coloridas.
  • VK_IMAGE_ASPECT_DEPTH_BIT: O aspecto de profundidade de uma imagem de estêncil e profundidade.
  • VK_IMAGE_ASPECT_STENCIL_BIT: O aspecto de estêncil de uma imagem de estêncil e profundidade.
  • VK_IMAGE_ASPECT_METADATA_BIT: Qualquer informação adicional associada à imagem que possa rastrear seu estado e é usada, por exemplo, em várias técnicas de compactação.

Como estamos tratando aqui somente de imagens coloridas, utilizamos apenas o sinalizador VK_IMAGE_ASPECT_COLOR_BIT.

viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;

Para criar uma nova image view que corresponda apenas a um subconjunto da cadeia MIP da imagem, devemos usar baseMipLevel e levelCount para especificar onde na cadeia MIP a exibição começa e quantos níveis MIP ela conterá. Se a imagem não tiver mapeamento MIP, como no nosso caso, esses campos deverão ser definidos como 0 e 1, respectivamente.

viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;

Da mesma forma, para criar uma image view de um subconjunto das camadas de array de uma imagem, usamos os campos baseArrayLayer e layerCount para especificar a camada inicial e o número de camadas, respectivamente. Novamente, se a imagem não for uma imagem de array, baseArrayLayer deve ser definido como 0 e layerCount deve ser definido como 1, como no nosso caso.

A criação do objeto VkImageView é realizada através de uma única chamada da função vkCreateImageView:

VkDevice device = m_window->device();
if (m_object->textureImageView) {
	m_deviceFunctions->vkDestroyImageView(
		device,
		m_object->textureImageView,
		nullptr
	);
}

VkResult result = m_deviceFunctions->vkCreateImageView(
	device,
	&viewInfo,
	nullptr,
	&m_object->textureImageView
);
if (result != VK_SUCCESS) {
	qFatal("Failed to create texture image view: %d", result);
}

Primeiro verificamos se o objeto VkImageView já foi criado. Caso já tenha sido criado, destruímos o objeto já criado, antes de chamar vkCreateImageView.

Destruímos esse objeto VkImageView em releaseObjectResources, antes de destruir o objeto de imagem em si:

void Renderer::releaseObjectResources() {
...
	if (m_object->textureImageView) {
		m_deviceFunctions->vkDestroyImageView(
			device,
			m_object->textureImageView,
			nullptr
		);
	}

    if (m_object->textureImage) {
        m_deviceFunctions->vkDestroyImage(
        	device,
        	m_object->textureImage,
        	nullptr
        );
        m_object->textureImage = VK_NULL_HANDLE;
    }
...


Anterior Próximo