Vulkan教程 - 20 图像采样器

2024-08-21 19:58

本文主要是介绍Vulkan教程 - 20 图像采样器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本章我们继续创建两个资源,用于图形管线采样图像。第一个资源是我们已经见过的,也就是和交换链图像打交道的时候用的,但是第二个则是新的,它和着色器如何从图像读取纹素有关。

我们之前就见到过,有了交换链图像和帧缓冲,图像可以通过图像视图访问而不是直接访问。也要为贴图图像创建这样一个图像视图。

添加一个类成员textureImageView存储贴图图像的VkImageView,然后创建一个新的方法createTextureImageView,在初始化Vulkan的创建贴图图像之后调用。

该方法的代码基本就和createImageView一样,就需要修改format和image两个字段:

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_UNORM;
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!");
}

由于有很多代码和createImageViews的一样,所以可以抽象一个新方法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_UNORM);
}

createImageViews简化为:

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

确保在程序结束的时候销毁图像视图,就在销毁图像本身之前:

vkDestroyImageView(device, textureImageView, nullptr);

着色器直接从图像读取纹素是可行的,但是当它们用于贴图的时候很少这么做。贴图通常用采样器访问,从而在计算最终获取到的颜色的时候能应用过滤和变换等。

这些过滤器对于应对过采样很有用。考虑一个映射到几何体的贴图,该贴图的片段比纹素要多。那么你就简单地在每个片段中为贴图坐标应用最近的纹素,那么你会得到类似第一个图这样的效果:

如果你联合4个最近的像素来进行线性插值,你可以得到一个平滑的效果,类似第二个图。当然可能你的程序就要第一种那样的艺术风格,例如我的世界,但是右侧的才是传统图形程序所喜欢的。当你从贴图读取颜色的时候,采样器对象能自动应用该过滤。

欠采样就是相反的问题了,就是纹素比片段多。这会在对高频图案采样的时候导致产生假象,比如象棋贴图在锐角观察的时候:

左边图像远处比较模糊,解决办法是各向异性过滤,这也是采样器自动应用的。

除了这些过滤器,采样器还能处理变换问题。当你想要通过寻址模式从图像外读取纹素的时候,它能决定要做些什么。下面这幅图展示了可能的操作:

现在我们创建一个新的方法createTextureSampler来建立采样器对象。我们会使用该采样器从着色器中的贴图读取颜色。我们把该方法放在创建贴图图像视图之后。

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来指定,可选的值如下:

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 = 16;

这两个字段指定了是否启用各向异性过滤。除非考虑性能问题,否则还是要启用的。maxAnisotropy字段限制了计算最终颜色的纹素样本个数。较小的值会有更好的性能,但是也会导致效果较差。当前图形硬件都不会用超过16个样本,因为那样区别就可以忽略了。

samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;

borderColor字段指定了当使用截断寻址模式时采样超过图像的时候返回什么颜色。可能会返回黑色、白色或者透明,格式是浮点或整数。你不能指定一个任意颜色。

samplerInfo.unnormalizedCoordinates = VK_FALSE;

unnormalizedCoordinates字段指定了你要用哪个坐标系统来对图像纹素寻址。如果该字段是VK_TRUE,你就能使用[0, texWidth)和[0, texHeight)取键的坐标。否则,纹素寻址在所有轴上都是[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;

所有这些字段都应用于Mip贴图。

采样器都定义好了,添加一个类成员来保存采样器对象句柄,然后用vkCreateSampler创建采样器:

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

注意采样器不会应用VkImage,采样器是一个独立的对象,提供了从贴图提取颜色的接口。它可以用于任何你想要的图片,不管是1D、2D或者3D。这和许多旧API不一样,它们会结合贴图图像并过滤到一个单独的状态中。

在程序结束时销毁采样器:

vkDestroySampler(device, textureSampler, nullptr);

这个就在cleanup方法中的销毁交换链之后。

现在运行程序,发现验证层提示:

这是因为各向异性过滤是可选设备特性,我们要更新逻辑设备创建部分的代码来请求该特性:

VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;

尽管不支持该特性在现代显卡上是不太可能发生的,但是我们还是要更新下isDeviceSuitable的代码来检查是否可用:

VkPhysicalDeviceFeatures supportedFeatures;
vkGetPhysicalDeviceFeatures(device, &supportedFeatures);return indices.isComplete() && extensionsSupported && swapChainAdequate&& supportedFeatures.samplerAnisotropy;

vkGetPhysicalDeviceFeatures重新调整了VkPhysicalDeviceFeatures的意图来表明支持哪个特性而不是通过设置布尔值来请求。

下一章我们会将图像和采样器对象暴露给着色器以将该贴图绘制到正方形上。

这篇关于Vulkan教程 - 20 图像采样器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1094140

相关文章

MQTT SpringBoot整合实战教程

《MQTTSpringBoot整合实战教程》:本文主要介绍MQTTSpringBoot整合实战教程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录MQTT-SpringBoot创建简单 SpringBoot 项目导入必须依赖增加MQTT相关配置编写

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

Logback在SpringBoot中的详细配置教程

《Logback在SpringBoot中的详细配置教程》SpringBoot默认会加载classpath下的logback-spring.xml(推荐)或logback.xml作为Logback的配置... 目录1. Logback 配置文件2. 基础配置示例3. 关键配置项说明Appender(日志输出器

Kali Linux安装实现教程(亲测有效)

《KaliLinux安装实现教程(亲测有效)》:本文主要介绍KaliLinux安装实现教程(亲测有效),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、下载二、安装总结一、下载1、点http://www.chinasem.cn击链接 Get Kali | Kal

Web技术与Nginx网站环境部署教程

《Web技术与Nginx网站环境部署教程》:本文主要介绍Web技术与Nginx网站环境部署教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Web基础1.域名系统DNS2.Hosts文件3.DNS4.域名注册二.网页与html1.网页概述2.HTML概述3.

spring security 超详细使用教程及如何接入springboot、前后端分离

《springsecurity超详细使用教程及如何接入springboot、前后端分离》SpringSecurity是一个强大且可扩展的框架,用于保护Java应用程序,尤其是基于Spring的应用... 目录1、准备工作1.1 引入依赖1.2 用户认证的配置1.3 基本的配置1.4 常用配置2、加密1. 密

WinForms中主要控件的详细使用教程

《WinForms中主要控件的详细使用教程》WinForms(WindowsForms)是Microsoft提供的用于构建Windows桌面应用程序的框架,它提供了丰富的控件集合,可以满足各种UI设计... 目录一、基础控件1. Button (按钮)2. Label (标签)3. TextBox (文本框

C#实现访问远程硬盘的图文教程

《C#实现访问远程硬盘的图文教程》在现实场景中,我们经常用到远程桌面功能,而在某些场景下,我们需要使用类似的远程硬盘功能,这样能非常方便地操作对方电脑磁盘的目录、以及传送文件,这次我们将给出一个完整的... 目录引言一. 远程硬盘功能展示二. 远程硬盘代码实现1. 底层业务通信实现2. UI 实现三. De

ubuntu20.0.4系统中安装Anaconda的超详细图文教程

《ubuntu20.0.4系统中安装Anaconda的超详细图文教程》:本文主要介绍了在Ubuntu系统中如何下载和安装Anaconda,提供了两种方法,详细内容请阅读本文,希望能对你有所帮助... 本文介绍了在Ubuntu系统中如何下载和安装Anaconda。提供了两种方法,包括通过网页手动下载和使用wg

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo