|
Grado en Ingeniería Informática
Animación por Ordenador
Curso 2025/2026
|
Práctica 2b
|
|
Creación de vistas
|
|
Objetivos
|
|
Añadir al proyecto la creación de vistas sobre las imágenes de la cadena de intercambio de
imágenes.
|
|
Vistas
|
|
Para poder acceder a una imagen y generar su contenido es necesario
crear un objeto VkImageView asociado a cada objeto
VkImage. Esto quiere decir que para que la aplicación pueda
generar las imágenes almacenadas en la swapchain es
necesario crear una lista de vistas asociadas a cada imagen.
Para crear una vista se utiliza la función vkCreateImageView().
VkResult vkCreateImageView (
VkDevice device,
const VkImageViewCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkImageView* pView);
|
La información necesaria para crear una vista se almacena en una
estructura VkImageViewCreateInfo. El contenido de sus
campos es el siguiente:
-
El campo sType debe ser
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO.
-
Los campos pNext y flags se han reservado para
futuras modificaciones y de momento deben declararse nulos.
-
El
campo image contiene la referencia a la imagen asociada
a la vista.
-
El campo viewType indica el tipo de imagen que se
quiere editar. Para una imagen asociada a una superficie lo
lógico es usar el tipo VK_IMAGE_VIEW_TYPE_2D, pero existen tipos
1D, 2D, 3D, CUBE, 1D_ARRAY, 2D_ARRAY y CUBE_ARRAY que se
utilizan en otros ámbitos.
-
El formato de la vista debe ser compatible con el formato de la
imagen. Generalmente se usa el mismo, aunque se consideran
formatos compatibles si tienen el mismo número de bits por
componente.
-
El campo components describe el mapeo a realizar sobre
los canales de color. La estructura VkComponentMapping contiene
cuatro campos (r,g,b,a) que permiten referenciar estos canales.
Estos campos deben recoger valores de la enumeración
VkComponentSwizzle. Por ejemplo, el valor
VK_COMPONENT_SWIZZLE_IDENTITY indica que el mapeo es al mismo
canal, pero también se puede indicar valores como
VK_COMPONENT_SWIZZLE_R para referirse a un canal concreto (el R
en este caso) o VK_COMPONENT_SWIZZLE_ZERO para indicar un valor
fijo (el cero en este caso).
-
El campo subresourceRange describe la forma en la que
se va a utilizar la vista de la imagen por medio de una
estructura VkImageSubresourceRange.
typedef struct VkImageViewCreateInfo {
VkStructureType sType;
const void* pNext;
VkImageViewCreateFlags flags;
VkImage image;
VkImageViewType viewType;
VkFormat format;
VkComponentMapping components;
VkImageSubresourceRange subresourceRange;
} VkImageViewCreateInfo;
|
La estructura VkImageSubresourceRange contiene el campo
aspectMask que indica el contenido que se va a almacenar en la
imagen. Este campo puede tomar los valores:
-
VK_IMAGE_ASPECT_COLOR_BIT
-
VK_IMAGE_ASPECT_DEPTH_BIT
-
VK_IMAGE_ASPECT_STENCIL_BIT
-
VK_IMAGE_ASPECT_METADATA_BIT
Los
campos baseMipLevel and levelCount para
seleccionar los niveles de mipmap utilizados y los campos
baseArrayLayer y layerCount para indicar las capas
utilizadas.
typedef struct VkImageSubresourceRange {
VkImageAspectFlags aspectMask;
uint32_t baseMipLevel;
uint32_t levelCount;
uint32_t baseArrayLayer;
uint32_t layerCount;
} VkImageSubresourceRange;
|
|
|
Modificaciones de la clase
GEDrawingContext
|
|
La
introducción de las vistas requiere modificar la clase
GEDrawingContext para añadir un vector de vistas y un método
para crearlas.
class GEDrawingContext
{
public:
std::vector<VkImageView> imageViews;
private:
// Campos auxiliares
uint32_t imageCount;
// Componentes gráficos
VkSwapchainKHR swapChain;
VkFormat imageFormat;
VkExtent2D imageExtent;
std::vector<VkImage> images;
...
private:
// Métodos de creación de componentes
void createSwapChain(GEGraphicsContext* gc, GEWindowPosition wpos);
void createImageViews(VkDevice device);
...
};
|
Los cambios en los métodos incluyen modificaciones en el constructor
de la clase para añadir la llamada a la creación de las vistas. Los
métodos destroy() y recreate() deben incluir la destrucción
y reconstrucción de todas las vistas. El método
createImageViews() se encarga de crear la lista de
vistas. Para ello crea un objeto VkImageView para cada una
de las imágenes de la swapchain.
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos públicos /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
// FUNCIÓN: GEDrawingContext::GEDrawingContext(...)
//
// PROPÓSITO: Crea el contexto de dibujo (imágenes y cadena de intercambio)
//
GEDrawingContext::GEDrawingContext(GEGraphicsContext* gc, GEWindowPosition wpos)
{
createSwapChain(gc, wpos);
createImageViews(gc->device);
std::cout << "Image views created!" << std::endl;
}
//
// FUNCIÓN: GEDrawingContext::destroy(GEGraphicsContext* gc)
//
// PROPÓSITO: Destruye los componentes del contexto de dibujo
//
void GEDrawingContext::destroy(GEGraphicsContext* gc)
{
for (uint32_t i = 0; i < imageCount; i++)
{
vkDestroyImageView(gc->device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(gc->device, swapChain, nullptr);
}
//
// FUNCIÓN: GEDrawingContext::recreate(GEGraphicsContext* gc, GEWindowPosition wpos)
//
// PROPÓSITO: Reconstruye los componentes del contexto de dibujo
//
void GEDrawingContext::recreate(GEGraphicsContext* gc, GEWindowPosition wpos)
{
for (uint32_t i = 0; i < imageCount; i++)
{
vkDestroyImageView(gc->device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(gc->device, swapChain, nullptr);
createSwapChain(gc, wpos);
createImageViews(gc->device);
}
...
///////////////////////////////////////////////////////////////////////////////////
///// /////
///// Métodos de inicialización de Vulkan /////
///// /////
///////////////////////////////////////////////////////////////////////////////////
...
//
// FUNCIÓN: GEDrawingContext::createImageViews(VkDevice device)
//
// PROPÓSITO: Crea una vista para cada imagen de la cadena de intercambio
//
void GEDrawingContext::createImageViews(VkDevice device)
{
imageViews.resize(imageCount);
for (size_t i = 0; i < imageCount; i++)
{
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = imageFormat;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &createInfo, nullptr, &imageViews[i])
!= VK_SUCCESS)
{
throw std::runtime_error("failed to create image views!");
}
}
}
|
|
|
Aspecto final
|
|
El
aspecto de la aplicación sigue siendo una ventana vacía. En la
consola se muestra el mensaje de creación de las vistas.
|
|