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

相关文章

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

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

基于Python开发一个图像水印批量添加工具

《基于Python开发一个图像水印批量添加工具》在当今数字化内容爆炸式增长的时代,图像版权保护已成为创作者和企业的核心需求,本方案将详细介绍一个基于PythonPIL库的工业级图像水印解决方案,有需要... 目录一、系统架构设计1.1 整体处理流程1.2 类结构设计(扩展版本)二、核心算法深入解析2.1 自

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤

Java JDK1.8 安装和环境配置教程详解

《JavaJDK1.8安装和环境配置教程详解》文章简要介绍了JDK1.8的安装流程,包括官网下载对应系统版本、安装时选择非系统盘路径、配置JAVA_HOME、CLASSPATH和Path环境变量,... 目录1.下载JDK2.安装JDK3.配置环境变量4.检验JDK官网下载地址:Java Downloads

使用Docker构建Python Flask程序的详细教程

《使用Docker构建PythonFlask程序的详细教程》在当今的软件开发领域,容器化技术正变得越来越流行,而Docker无疑是其中的佼佼者,本文我们就来聊聊如何使用Docker构建一个简单的Py... 目录引言一、准备工作二、创建 Flask 应用程序三、创建 dockerfile四、构建 Docker

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性: