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

相关文章

Ubuntu固定虚拟机ip地址的方法教程

《Ubuntu固定虚拟机ip地址的方法教程》本文详细介绍了如何在Ubuntu虚拟机中固定IP地址,包括检查和编辑`/etc/apt/sources.list`文件、更新网络配置文件以及使用Networ... 1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安

PyCharm 接入 DeepSeek最新完整教程

《PyCharm接入DeepSeek最新完整教程》文章介绍了DeepSeek-V3模型的性能提升以及如何在PyCharm中接入和使用DeepSeek进行代码开发,本文通过图文并茂的形式给大家介绍的... 目录DeepSeek-V3效果演示创建API Key在PyCharm中下载Continue插件配置Con

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

MySql9.1.0安装详细教程(最新推荐)

《MySql9.1.0安装详细教程(最新推荐)》MySQL是一个流行的关系型数据库管理系统,支持多线程和多种数据库连接途径,能够处理上千万条记录的大型数据库,本文介绍MySql9.1.0安装详细教程,... 目录mysql介绍:一、下载 Mysql 安装文件二、Mysql 安装教程三、环境配置1.右击此电脑

在idea中使用mysql数据库超详细教程

《在idea中使用mysql数据库超详细教程》:本文主要介绍如何在IntelliJIDEA中连接MySQL数据库,并使用控制台执行SQL语句,还详细讲解了如何使用MyBatisGenerator快... 目录一、连接mysql二、使用mysql三、快速生成实体、接口、sql文件总结一、连接mysql在ID