操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Visual Studio 2017


在本章节我们将为图形管线创建另外两个资源来对图像进行采样。第一个资源我们之前已经接触过了,就是交换链,但是第二个资源比较新,它涉及着色器如何从图像中读取纹素。

Texture image view


我们之前看过交换链和帧缓冲区,图像不是直接访问,而是通过图像视图。我们也会借助图像视图来访问纹理图像。

添加一个类成员变量vkImageView保存纹理图像,并且创建新的函数createTextureImageView

  1. VkImageView textureImageView;
  2.  
  3. ...
  4.  
  5. void initVulkan() {
  6. ...
  7. createTextureImage();
  8. createTextureImageView();
  9. createVertexBuffer();
  10. ...
  11. }
  12.  
  13. ...
  14.  
  15. void createTextureImageView() {
  16.  
  17. }

函数中的代码可以主要基于createImageViews。仅有的两个变化是formatimage字段:

  1. VkImageViewCreateInfo viewInfo = {};
  2. viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  3. viewInfo.image = textureImage;
  4. viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
  5. viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
  6. viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  7. viewInfo.subresourceRange.baseMipLevel = ;
  8. viewInfo.subresourceRange.levelCount = ;
  9. viewInfo.subresourceRange.baseArrayLayer = ;
  10. viewInfo.subresourceRange.layerCount = ;

这里已经省略了显示的 viewInfo.components 初始化,因为VK_COMPONET_SWIZZLE_IDENTITY被定义为0。最后在函数中通过调用vkCreateImageView完成图像视图的创建:

  1. if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
  2. throw std::runtime_error("failed to create texture image view!");
  3. }

因为很多逻辑都是从createImageViews复制过来的,所以可以抽象一个新的函数createImageView封装该部分逻辑。

  1. VkImageView createImageView(VkImage image, VkFormat format) {
  2. VkImageViewCreateInfo viewInfo = {};
  3. viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  4. viewInfo.image = image;
  5. viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
  6. viewInfo.format = format;
  7. viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  8. viewInfo.subresourceRange.baseMipLevel = ;
  9. viewInfo.subresourceRange.levelCount = ;
  10. viewInfo.subresourceRange.baseArrayLayer = ;
  11. viewInfo.subresourceRange.layerCount = ;
  12.  
  13. VkImageView imageView;
  14. if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
  15. throw std::runtime_error("failed to create texture image view!");
  16. }
  17.  
  18. return imageView;
  19. }

createTextureImageView函数可以简化为:

  1. void createTextureImageView() {
  2. textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM);
  3. }

createImageViews可以简化为:

  1. void createImageViews() {
  2. swapChainImageViews.resize(swapChainImages.size());
  3.  
  4. for (uint32_t i = ; i < swapChainImages.size(); i++) {
  5. swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
  6. }
  7. }

确保程序退出的时候销毁图像视图,并在销毁图像本身前清理图像视图。

Samplers


着色器直接从图像中读取纹素是可以的,但是当它们作为纹理图像的时候并不常见。纹理图像通常使用采样器来访问,应用过滤器和变换来计算最终的颜色。

这些过滤器有助于处理超载采样的问题。考虑一个映射到几何图形的纹理图像,拥有比纹素更多的片元。如果只是在每个片段中使用最接近的纹理坐标,那么会获得第一个图像的结果:

如果混合最近的四个纹素通过显性插值,我们会看到更加平滑的结果,如右图所示。当然,应用程序可能具有符合左侧风格的艺术要求(比如Minecraft),但是常规的图形应用程序中更倾向右侧的效果。当从纹理中读取一个颜色的时候,采样器自动应用过滤器。

如果采样负载采样也会造成问题。当采样频率过高的时候,比如对于棋盘的纹理进行采样,会导致在有锐度角的地方产生幻影。

如作左侧图示,顺着距离的变化,纹理变的模糊且混乱的。解决方案是各向异性过滤 anisotropic filtering,它会自动被采样器应用。

除了这些过滤器,采样器也参与变换。当尝试读取图像外的纹素的时候,采用什么 addressing mode 寻址模式 。下图显示了一些可能的模式:

添加新函数createTextureSampler配置采样器对象。我们稍后会使用它从着色器中读取颜色。

  1. void initVulkan() {
  2. ...
  3. createTextureImage();
  4. createTextureImageView();
  5. createTextureSampler();
  6. ...
  7. }
  8.  
  9. ...
  10.  
  11. void createTextureSampler() {
  12.  
  13. }

采样器通过VkSamplerCreateInfo结构体配置,它用来指定将要应用的过滤器和变换。

  1. VkSamplerCreateInfo samplerInfo = {};
  2. samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
  3. samplerInfo.magFilter = VK_FILTER_LINEAR;
  4. samplerInfo.minFilter = VK_FILTER_LINEAR;

magFilter 和 minFilter 过滤器字段指定纹素放大和缩小内插值方式。放大关注上文描述的超采样问题,缩小关注负载采样的问题。VK_FILTER_NEARESTVK_FILTER_LINEAR是可选的选项,对应上面图片纰漏的模式。

  1. samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
  2. samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
  3. samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;

可以使用addressMode字段指定每个轴向使用的寻址模式。有效的值列在下方。大多数在图像中已经解释说明过了。需要注意的是轴向在这里称为 U,V 和 W 代替 X,Y 和 Z。这是纹理空间坐标的约定。

  • VK_SAMPLER_ADDRESS_MODE_REPEAT:当超过图像尺寸的时候采用循环填充。
  • VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:与循环模式类似,但是当超过图像尺寸的时候,它采用反向镜像效果。
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:当超过图像尺寸的时候,采用边缘最近的颜色进行填充。
  • VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TOEDGE:与边缘模式类似,但是使用与最近边缘相反的边缘进行填充。
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:当采样超过图像的尺寸时,返回一个纯色填充。

在这里使用什么样的寻址模式并不重要,因为我们不会在图像之外进行采样。但是循环模式是普遍使用的一种模式,因为它可以用来实现诸如瓦片地面和墙面的纹理效果。

  1. samplerInfo.anisotropyEnable = VK_TRUE;
  2. samplerInfo.maxAnisotropy = ;

这两个字段指定是否使用各向异性过滤器。没有理由不使用该特性,除非性能是一个问题。maxAnisotropy字段限制可用于计算最终颜色的纹素采样的数量。低的数值会得到比较好的性能,但是会得到较差的质量。当前没有任何的图形硬件可以使用超过16个采样器,因为与其超过16个采样器之间的差异可以忽略不计。

  1. samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;

borderColor字段指定采样范围超过图像时候返回的颜色,与之对应的是边缘寻址模式。可以以float或者int格式返回黑色,白色或者透明度。但是不能指定任意颜色。

  1. samplerInfo.unnormalizedCoordinates = VK_FALSE;

unnormalizedCoordinates字段指定使用的坐标系统,用于访问图像的纹素。如果字段为VK_TRUE,意味着可以简单的使用坐标范围为 [ 0, texWidth ) [ 0, texHeight )。如果使用VK_FALSE,意味着每个轴向纹素访问使用  [ 0, 1) 范围。真实的应用程序总是使用归一化的坐标。因为这样可以使用完全相同坐标的不同分辨率的纹理。

  1. samplerInfo.compareEnable = VK_FALSE;
  2. samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

如果开启比较功能,那么纹素首先和值进行比较,并且比较后的值用于过滤操作。主要用在阴影纹理映射的 percentage-closer filtering 即百分比近似过滤器。我们会在未来的章节中看到。

  1. samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
  2. samplerInfo.mipLodBias = 0.0f;
  3. samplerInfo.minLod = 0.0f;
  4. samplerInfo.maxLod = 0.0f;

所有这些字段应用在mipmapping。mipmapping也在未来章节中看到,但是基本的它可以应用另一种滤波器。

采样器的功能现在已经完整的定义了。添加类成员持有采样器对象的引用并通过vkCreateSampler创建采样器:

  1. VkImageView textureImageView;
  2. VkSampler textureSampler;
  3.  
  4. ...
  5.  
  6. void createTextureSampler() {
  7. ...
  8.  
  9. if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
  10. throw std::runtime_error("failed to create texture sampler!");
  11. }
  12. }

需要注意的是采样器没有任何地方引用VkImage。采样器是一个独特的对象,它提供了从纹理中提取颜色的接口。它可以应用在任何你期望的图像中,无论是1D,2D,或者是3D。也与之前很多旧的API是不同的,后者将纹理图像与过滤器混合成单一状态。

在程序的最后且不再访问图像的时候,销毁采样器:

  1. void cleanup() {
  2. cleanupSwapChain();
  3.  
  4. vkDestroySampler(device, textureSampler, nullptr);
  5. vkDestroyImageView(device, textureImageView, nullptr);
  6.  
  7. ...
  8. }

Anisotropy device feature


如果现在运行程序,你会看到validation layer消息如下:

这是因为各向异性滤波器是一个可选的特性。我们需要更新createLogicalDevice函数请求它:

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

并且尽管现在的图形卡不太可能不支持该功能,但建议仍然更新isDeviceSuitable函数去检测是否有效:

  1. bool isDeviceSuitable(VkPhysicalDevice device) {
  2. ...
  3.  
  4. VkPhysicalDeviceFeatures supportedFeatures;
  5. vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
  6.  
  7. return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy;
  8. }

vkGetPhysicalDeviceFeaturesVkPhysicalDeviceFeatures结构重新定义,指定哪些特性被支持而不是通过设置boolean值来请求。

如果不是强制使用各向异性滤波器,也可以简单的通过条件设定来不使用它:

  1. samplerInfo.anisotropyEnable = VK_FALSE;
  2. samplerInfo.maxAnisotropy = ;

下一章我们将图像与采样器对象公开到着色器中,绘制纹理到正方形上。

项目代码 GitHub地址。

Vulkan Tutorial 26 Image view and sampler的更多相关文章

  1. Vulkan Tutorial 26 view and sampler

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 在本章节我们将为图形管线创建另外两个资源来对图像进行采样.第一个资源我们之前已经接触 ...

  2. [译]Vulkan教程(26)描述符池和set

    [译]Vulkan教程(26)描述符池和set Descriptor pool and sets 描述符池和set Introduction 入门 The descriptor layout from ...

  3. Vulkan Tutorial 27 combined image sampler

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们在教程的uniform 缓冲区中首次了解了描述符.在本 ...

  4. Vulkan Tutorial 23 Descriptor layout and buffer

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 我们现在可以将任意属性传递给每个顶点的顶点着色器使用.但是 ...

  5. Vulkan Tutorial 28 Depth buffering

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 到目前为止,我们所使用的几何图形为3D,但仍然完全扁平的. ...

  6. Vulkan Tutorial 29 Loading models

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 应用程序现在已经可以渲染纹理3D模型,但是 vertice ...

  7. Vulkan Tutorial 开发环境搭建之Windows

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么 ...

  8. Vulkan Tutorial 02 编写Vulkan应用程序框架原型

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 General structure 在上一节中,我们创建了一个正确配置.可运行的的V ...

  9. Vulkan Tutorial 05 物理设备与队列簇

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Selecting a physical device 通过VkInstance初始 ...

随机推荐

  1. 推荐近乎免费的调试神器——OzCode

    当一只断点打在 Visual Studio 的代码编辑器中,程序命中断点的那一刻,调试才刚刚开始……这个时候忙碌的手在键盘和鼠标之间来回跳跃,试图抓住每一次单步执行带来的状态改变. 如果命中断点的那一 ...

  2. Quartz 2D编程指南(2) - 图形上下文

    一个Graphics Context表示一个绘制目标.它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息 ...

  3. 实例-QPSK的fpga实现

  4. drone 学习四 几个有用的命令

    1. 安装cli 工具 linux curl -L https://github.com/drone/drone-cli/releases/download/v0.8.5/drone_linux_am ...

  5. Cocos2d-x第一次调试出现的问题

    1.无法打开包括文件:“CCStdC.h“ http://blog.csdn.net/jbboy/article/details/9773087 2.无法打开文件“libcocos2d.lib” 用v ...

  6. YARN的Fair Scheduler和Capacity Scheduler

    关于Scheduler YARN有四种调度机制:Fair Schedule,Capacity Schedule,FIFO以及Priority: 其中Fair Scheduler是资源池机制,进入到里面 ...

  7. windows环境vagrant修改静态资源文件,centos虚拟机中nginx的web环境下不生效

    最近上手krpano,本地修改了krpano.html文件或者xml文件,在虚拟机环境打开文件是修改过来了,在nginx中就是不生效. 修改nginx.conf中http{}中的 sendfile  ...

  8. python3api-ms-win-crt-runtime-l1-1-0.dll丢失解决方法

    先记录一个之前遇到的问题: 在安装了pycharm后,发现 通过上网发现,其实就是没有安装pip和setuptools,其实 Python3以后都是默认安装pip的,所以最后的解决办法是将我目前的Py ...

  9. 全排列函数C++实现

    例题:求由123456789构成的所有九位数字 1 用C++的next_permutation函数 #include <iostream> #include <stdio.h> ...

  10. Service的用法

    基本用法: 1.创建一个类继承Service类,并重写onBind() 2.重写其他方法:onCreate().onStartCommand().onDestory() 3.在AndroidManif ...