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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

沁恒CH32在MounRiver Studio上环境配置以及使用详细教程

目录 1.  RISC-V简介 2.  CPU架构现状 3.  MounRiver Studio软件下载 4.  MounRiver Studio软件安装 5.  MounRiver Studio软件介绍 6.  创建工程 7.  编译代码 1.  RISC-V简介         RISC就是精简指令集计算机(Reduced Instruction SetCom

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服务端运行。 less的中文官网:https://lesscss.cn/ 2. less编译工具 koala 官网 http://koala-app.

【Shiro】Shiro 的学习教程(三)之 SpringBoot 集成 Shiro

目录 1、环境准备2、引入 Shiro3、实现认证、退出3.1、使用死数据实现3.2、引入数据库,添加注册功能后端代码前端代码 3.3、MD5、Salt 的认证流程 4.、实现授权4.1、基于角色授权4.2、基于资源授权 5、引入缓存5.1、EhCache 实现缓存5.2、集成 Redis 实现 Shiro 缓存 1、环境准备 新建一个 SpringBoot 工程,引入依赖:

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param