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

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

电脑提示d3dx11_43.dll缺失怎么办? DLL文件丢失的多种修复教程

《电脑提示d3dx11_43.dll缺失怎么办?DLL文件丢失的多种修复教程》在使用电脑玩游戏或运行某些图形处理软件时,有时会遇到系统提示“d3dx11_43.dll缺失”的错误,下面我们就来分享超... 在计算机使用过程中,我们可能会遇到一些错误提示,其中之一就是缺失某个dll文件。其中,d3dx11_4

Linux下在线安装启动VNC教程

《Linux下在线安装启动VNC教程》本文指导在CentOS7上在线安装VNC,包含安装、配置密码、启动/停止、清理重启步骤及注意事项,强调需安装VNC桌面以避免黑屏,并解决端口冲突和目录权限问题... 目录描述安装VNC安装 VNC 桌面可能遇到的问题总结描js述linux中的VNC就类似于Window