Texture mapping/Image view and sampler

本文介绍了在Vulkan中创建纹理图像视图和采样器的过程,包括如何为纹理图像创建ImageView,以及如何配置和创建用于过滤和变换的Sampler。各向异性过滤被提及作为解决欠采样和过采样问题的一种方法,同时强调了需要根据设备特性来启用或禁用该功能。
摘要由CSDN通过智能技术生成

目录

Texture image view

Samplers

Anisotropy device feature


在本章中,我们将创建两个额外的资源,用于图形管道采样图像。第一个资源是我们在处理交换链图像时已经看到的资源,但第二个资源是新的-它与着色器如何从图像中读取纹理有关。

Texture image view

我们之前已经看到,使用交换链图像和帧缓冲区,图像是通过图像视图而不是直接访问的。我们还需要为纹理图像创建这样的图像视图。

添加一个类成员以保存纹理图像的VkImageView,并创建一个新函数createTextureImageView,我们将在其中创建它:

VkImageView textureImageView;

...

void initVulkan() {
    ...
    createTextureImage();
    createTextureImageView();
    createVertexBuffer();
    ...
}

...

void createTextureImageView() {

}

此函数的代码可以直接基于createImageViews。您只需对格式和图像进行两个更改:

VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;

我省略了显式viewInfo.components初始化,因为VK_COMPONENT_SWIZZLE_IDENTITY无论如何都定义为0。通过调用vkCreateImageView完成创建图像视图:

if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
    throw std::runtime_error("failed to create texture image view!");
}

由于createImageView中复制了这么多逻辑,您可能希望将其抽象为一个新的createImageView函数:

VkImageView createImageView(VkImage image, VkFormat format) {
    VkImageViewCreateInfo viewInfo{};
    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewInfo.image = image;
    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewInfo.format = format;
    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    viewInfo.subresourceRange.baseMipLevel = 0;
    viewInfo.subresourceRange.levelCount = 1;
    viewInfo.subresourceRange.baseArrayLayer = 0;
    viewInfo.subresourceRange.layerCount = 1;

    VkImageView imageView;
    if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture image view!");
    }

    return imageView;
}

createTextureImageView函数现在可以简化为:

void createTextureImageView() {
    textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB);
}

createImageViews可以简化为:

void createImageViews() {
    swapChainImageViews.resize(swapChainImages.size());

    for (uint32_t i = 0; i < swapChainImages.size(); i++) {
        swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
    }
}

确保在程序结束时销毁图像视图,然后再销毁图像本身:

void cleanup() {
    cleanupSwapChain();

    vkDestroyImageView(device, textureImageView, nullptr);

    vkDestroyImage(device, textureImage, nullptr);
    vkFreeMemory(device, textureImageMemory, nullptr);

Samplers

着色器可以直接从图像中读取纹素,但当它们用作纹理时,这不是很常见。纹理通常通过采样器访问,采样器将应用过滤和变换来计算检索到的最终颜色。

这些过滤器有助于处理过采样等问题。考虑映射到片段多于纹理像素的几何体的纹理。如果您只需为每个片段中的纹理坐标取最近的纹素,则会得到类似于第一幅图像的结果:

如果通过线性插值组合4个最接近的纹素,则会得到像右侧的结果一样的平滑结果。当然,您的应用程序可能有更适合左侧风格的艺术风格要求(想想Minecraft),但在传统的图形应用程序中,右侧是首选的。当从纹理中读取颜色时,采样器对象会自动为您应用此过滤。

欠采样是相反的问题,在这种情况下,纹理像素比碎片多。这将导致在以锐角采样高频图案(如棋盘纹理)时出现伪影:

如左图所示,纹理在远处变成了模糊的混乱。解决这个问题的方法是各向异性滤波,它也可以由采样器自动应用。

除了这些过滤器,采样器还可以处理转换。它决定了当您试图通过其寻址模式读取图像外部的纹素时会发生什么。下图显示了一些可能性:

现在我们将创建一个函数createTextureSampler来设置这样的采样器对象。稍后我们将使用该采样器从着色器中的纹理中读取颜色。

void initVulkan() {
    ...
    createTextureImage();
    createTextureImageView();
    createTextureSampler();
    ...
}

...

void createTextureSampler() {

}

采样器通过VkSamplerCreateInfo结构进行配置,该结构指定了应该应用的所有过滤器和转换。

VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;

magFilter和minFilter字段指定如何插值放大或缩小的纹素。放大与上述过采样问题有关,缩小与欠采样有关。选项为VK_FILTER_NEAREST和VK_FILTER_LINEAR,对应于上图所示的模式。

samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;

可以使用addressMode字段按轴指定寻址模式。可用值如下所示。上面的图片展示了其中的大部分。请注意,轴被称为U、V和W,而不是X、Y和Z。这是纹理空间坐标的惯例。

  • VK_SAMPLER_ADDRESS_MODE_REPEAT:超出图像尺寸时重复纹理。
  • VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:类似于重复,但在超出维度时反转坐标以镜像图像。
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:获取图像尺寸以外最接近坐标的边的颜色。
  • VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:类似于钳夹到边,但使用与最近边相对的边。
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:当采样超出图像的维度时,返回纯色。

我们在这里使用哪种寻址模式并不重要,因为在本教程中,我们不会在图像之外进行采样。然而,重复模式可能是最常见的模式,因为它可以用于平铺地板和墙壁等纹理。

samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = ???;

这两个字段指定是否应使用各向异性过滤。除非性能是一个问题,否则没有理由不使用它。max各向异性字段限制可用于计算最终颜色的纹素样本数量。值越低,性能越好,但质量越低。要确定可以使用的值,我们需要检索物理设备的特性,如下所示:

VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(physicalDevice, &properties);

如果查看VkPhysicalDeviceProperties结构的文档,您会发现它包含一个名为limits的VkPhysicalDeviceLimits成员。这个结构又有一个名为maxSamplerAnistropy的成员,这是我们可以为maxAnisotropy指定的最大值。如果我们想追求最高质量,我们可以直接使用该值:

samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;

您可以在程序开始时查询特性并将其传递给需要它们的函数,也可以在createTextureSampler函数本身中查询它们。

samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;

borderColor字段指定在使用箝位到边框寻址模式对图像进行采样时返回的颜色。可以以float或int格式返回黑色、白色或透明。不能指定任意颜色。

samplerInfo.unnormalizedCoordinates = VK_FALSE;

unnormalizedCoordinates字段指定要用于处理图像中的纹素的坐标系。如果此字段为VK_TRUE,则可以简单地使用[0,texWidth)和[0,texHeight)范围内的坐标。如果为VK_FALSE,则使用所有轴上的[0,1)范围来处理纹理。现实世界的应用程序几乎总是使用标准化坐标,因为这样就可以使用具有完全相同坐标的不同分辨率的纹理。

samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

如果启用了比较功能,则纹素将首先与值进行比较,比较结果将用于过滤操作。这主要用于阴影贴图上的百分比接近过滤。我们将在以后的章节中讨论这一点。

samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;

所有这些字段都适用于mipmapping。我们将在后面的章节中讨论mipmapping,但基本上它是可以应用的另一种类型的过滤器。

采样器的功能现在已完全定义。添加一个类成员以保持采样器对象的句柄,并使用vkCreateSampler创建采样器:

VkImageView textureImageView;
VkSampler textureSampler;

...

void createTextureSampler() {
    ...

    if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture sampler!");
    }
}

注意,采样器在任何地方都不引用VkImage。采样器是一个独特的对象,它提供了从纹理中提取颜色的界面。它可以应用于您想要的任何图像,无论是1D、2D还是3D。这与许多旧的API不同,后者将纹理图像和过滤合并为单一状态。

当我们不再访问图像时,在程序结束时销毁采样器:

void cleanup() {
    cleanupSwapChain();

    vkDestroySampler(device, textureSampler, nullptr);
    vkDestroyImageView(device, textureImageView, nullptr);

    ...
}

Anisotropy device feature

如果您现在运行程序,您将看到如下验证层消息:

这是因为各向异性过滤实际上是可选的设备功能。我们需要更新createLogicalDevice函数以请求它:

VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = VK_TRUE;

即使现代显卡不太可能支持它,我们也应该更新isDeviceFit以检查它是否可用:

bool isDeviceSuitable(VkPhysicalDevice device) {
    ...

    VkPhysicalDeviceFeatures supportedFeatures;
    vkGetPhysicalDeviceFeatures(device, &supportedFeatures);

    return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
}

vkGetPhysicalDeviceFeatures重新调整了VkPhysicalDeviceFunctions结构的用途,以通过设置布尔值来指示支持哪些功能,而不是请求哪些功能。

不必强制使用各向异性过滤,也可以通过有条件地设置不使用它:

samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;

在下一章中,我们将向着色器公开图像和采样器对象,以将纹理绘制到正方形上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值