操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Visual Studio 2017


Introduction

我们在教程的 uniform buffer 章节中首次了解了描述符。在本章节我们会看到一种新的描述符类型:combined image sampler 组合图像取样器。该描述符使着色器通过类似上一章创建的采样器对象那样,来访问图像资源。

我们将首先修改描述符布局,描述符对象池和描述符集合,以包括这样一个组合的图像采样器描述符。完成之后,我们会添加纹理贴图坐标到Vertex数据中,并修改片段着色器从纹理中读取颜色,而不是内插顶点颜色。

Updating the descriptors


浏览到createDesriptorSetLayout函数,并为组合图像采样器描述符添加一个VkDescriptorSetLayoutBinding。我们将简单的在uniform buffer之后进行绑定操作。

  1. VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
  2. samplerLayoutBinding.binding = ;
  3. samplerLayoutBinding.descriptorCount = ;
  4. samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  5. samplerLayoutBinding.pImmutableSamplers = nullptr;
  6. samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
  7.  
  8. std::array<VkDescriptorSetLayoutBinding, > bindings = {uboLayoutBinding, samplerLayoutBinding};
  9. VkDescriptorSetLayoutCreateInfo layoutInfo = {};
  10. layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  11. layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
  12. layoutInfo.pBindings = bindings.data();

确保stageFlags正确设置,指定我们打算在片段着色器中使用组合图像采样器描述符。这就是片段颜色最终被确定的地方。可以在顶点着色器中使用纹理采样,比如,通过高度图 heightmap 动态的变形顶点的网格。

如果你开启validation layers运行程序,你将会看到它引起了描述符对象池不能使用这个布局分配描述符集合,因为它没有任何组合图像采样器描述符。来到createDescriptorPool函数,以包含此描述符的VkDescriptorPoolSize

  1. std::array<VkDescriptorPoolSize, > poolSizes = {};
  2. poolSizes[].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  3. poolSizes[].descriptorCount = ;
  4. poolSizes[].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  5. poolSizes[].descriptorCount = ;
  6.  
  7. VkDescriptorPoolCreateInfo poolInfo = {};
  8. poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  9. poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
  10. poolInfo.pPoolSizes = poolSizes.data();
  11. poolInfo.maxSets = ;

最后一步是将实际的图像和采样器资源绑定到描述符集合中的具体描述符。来到createDescriptorSet函数。

  1. VkDescriptorImageInfo imageInfo = {};
  2. imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  3. imageInfo.imageView = textureImageView;
  4. imageInfo.sampler = textureSampler;

组合图像采样器结构体的资源必须在VkDescriptorImageInfo结构进行指定。就像在VkDescriptorBufferInfo结构体中指定一个 uniform buffer descriptor 缓冲区资源一样。这是上一章节中的对象汇集的代码段。

  1. std::array<VkWriteDescriptorSet, > descriptorWrites = {};
  2.  
  3. descriptorWrites[].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  4. descriptorWrites[].dstSet = descriptorSet;
  5. descriptorWrites[].dstBinding = ;
  6. descriptorWrites[].dstArrayElement = ;
  7. descriptorWrites[].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  8. descriptorWrites[].descriptorCount = ;
  9. descriptorWrites[].pBufferInfo = &bufferInfo;
  10.  
  11. descriptorWrites[].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  12. descriptorWrites[].dstSet = descriptorSet;
  13. descriptorWrites[].dstBinding = ;
  14. descriptorWrites[].dstArrayElement = ;
  15. descriptorWrites[].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  16. descriptorWrites[].descriptorCount = ;
  17. descriptorWrites[].pImageInfo = &imageInfo;
  18.  
  19. vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), , nullptr);

描述符必须与此图像信息一起更新,就像缓冲区一样。这次我们使用pImageInfo数组代替pBufferInfo。描述符现在可以被着色器使用!

Texture coordinates


纹理映射的一个重要组成部分仍然缺少,这是每个顶点的实际坐标。坐标决定如何实际的映射到几何图形上。

  1. struct Vertex {
  2. glm::vec2 pos;
  3. glm::vec3 color;
  4. glm::vec2 texCoord;
  5.  
  6. static VkVertexInputBindingDescription getBindingDescription() {
  7. VkVertexInputBindingDescription bindingDescription = {};
  8. bindingDescription.binding = ;
  9. bindingDescription.stride = sizeof(Vertex);
  10. bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
  11.  
  12. return bindingDescription;
  13. }
  14.  
  15. static std::array<VkVertexInputAttributeDescription, > getAttributeDescriptions() {
  16. std::array<VkVertexInputAttributeDescription, > attributeDescriptions = {};
  17.  
  18. attributeDescriptions[].binding = ;
  19. attributeDescriptions[].location = ;
  20. attributeDescriptions[].format = VK_FORMAT_R32G32_SFLOAT;
  21. attributeDescriptions[].offset = offsetof(Vertex, pos);
  22.  
  23. attributeDescriptions[].binding = ;
  24. attributeDescriptions[].location = ;
  25. attributeDescriptions[].format = VK_FORMAT_R32G32B32_SFLOAT;
  26. attributeDescriptions[].offset = offsetof(Vertex, color);
  27.  
  28. attributeDescriptions[].binding = ;
  29. attributeDescriptions[].location = ;
  30. attributeDescriptions[].format = VK_FORMAT_R32G32_SFLOAT;
  31. attributeDescriptions[].offset = offsetof(Vertex, texCoord);
  32.  
  33. return attributeDescriptions;
  34. }
  35. };

修改Vertex结构体包含vec2结构用于纹理坐标。确保加入VkVertexInputAttributeDescription结构体,如此我们就可以在顶点着色器中访问纹理UV坐标数据。这是必要的,以便能够将它们传递到片段着色器,以便在正方形的表面进行插值处理。

  1. const std::vector<Vertex> vertices = {
  2. {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
  3. {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
  4. {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
  5. {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
  6. };

在本教程中,使用坐标从左上角 0,0 到右下角的 1,1 来映射纹理,从而简单的填充矩形。在这里可以尝试各种坐标。尝试使用低于 0 或者 1 以上的坐标来查看寻址模式的不同表现。

Shaders


最后一步是修改着色器从纹理中采样颜色。我们首先需要修改顶点着色器,传递纹理坐标到片段着色器。

  1. layout(location = ) in vec2 inPosition;
  2. layout(location = ) in vec3 inColor;
  3. layout(location = ) in vec2 inTexCoord;
  4.  
  5. layout(location = ) out vec3 fragColor;
  6. layout(location = ) out vec2 fragTexCoord;
  7.  
  8. void main() {
  9. gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
  10. fragColor = inColor;
  11. fragTexCoord = inTexCoord;
  12. }

就像每个顶点的颜色,fragTexCoord值通过光栅化平滑的插值到矩形区域内。我们可以通过使片段着色器输出的纹理坐标为颜色来可视化看到这些:

  1. #version
  2. #extension GL_ARB_separate_shader_objects : enable
  3.  
  4. layout(location = ) in vec3 fragColor;
  5. layout(location = ) in vec2 fragTexCoord;
  6.  
  7. layout(location = ) out vec4 outColor;
  8.  
  9. void main() {
  10. outColor = vec4(fragTexCoord, 0.0, 1.0);
  11. }

现在应该可以看到如下图所示的效果。不要忘记重新编译着色器!

绿色通道代表水平坐标,红色通道代表垂直坐标。黑色和黄色角确认了纹理坐标正确的从 0,01,1 进行插值填充到方形中。使用颜色可视化在着色器中编程等价于 printf 调试,除此之外没有更好的方法!

组合图像采样器描述符在GLSL中通过采样器 uniform代替。在片段着色器中引用它:

  1. layout(binding = ) uniform sampler2D texSampler;

对于其他图像有等价的 sampler1Dsampler3D 类型。确保正确的绑定操作。

  1. void main() {
  2. outColor = texture(texSampler, fragTexCoord);
  3. }

纹理采用内置的 texture 函数进行采样。它需要使用 sampler 和 坐标作为参数。采样器在后台自动处理过滤和变化功能。你应该可以看到纹理贴图在方形上:

尝试修改寻址模式放大大于 1 来观测效果。比如,下面的片段着色器输出的结果使用VK_SAMPLER_ADDRESS_MODE_REPEAT

  1. void main() {
  2. outColor = texture(texSampler, fragTexCoord * 2.0);
  3. }

还可以使用顶点颜色来处理纹理颜色:

  1. void main() {
  2. outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0);
  3. }

将RGB和alha通道分离开,以便不缩放alpha通道。

现在已经知道如何在着色器中访问图像!当与帧缓冲区中的图像进行结合时,这是一个非常有效的技术。你可以看到这些图像作为输入实现很酷的效果,比如 post-processing和3D世界摄像机显示。

项目代码 GitHub地址。

Vulkan Tutorial 27 combined image sampler的更多相关文章

  1. Vulkan Tutorial 26 view and sampler

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

  2. [译]Vulkan教程(27)Image

    [译]Vulkan教程(27)Image Images Introduction 入门 The geometry has been colored using per-vertex colors so ...

  3. Vulkan Tutorial 26 Image view and sampler

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

  4. Vulkan Tutorial 29 Loading models

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

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

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

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

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

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

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

  8. Vulkan Tutorial 05 逻辑设备与队列

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 在选择要使用的物理设备之后,我们需要设置一个逻辑设备用于交 ...

  9. Vulkan Tutorial 07 Window surface

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 到目前为止,我们了解到Vulkan是一个与平台特性无关联的API集合.它不能直接与窗 ...

随机推荐

  1. SpringMVC 3.2集成Spring Security 3.2集成mybaties

    目录结构如下

  2. (基础篇 走进javaNIO)第一章-java的i/o演进之路

    Java 是由 SUN公司在 1995 年首先发布 的编程语 言和计算平 台.这基础技术 支持最新 的程序 ,包括 实用程序 .游 戏和业 务应用程序 .J ava 在世界各地 的 8.5  亿 多 ...

  3. mysql数据库小常识

    什么是数据库? 计算机处理和存储的一切信息都是数据. 计算机系统中一种用于存储数据的程序. 一种:计算机系统中有很多种能够存取数据的程序. 他们各有特长和长处,有自己的适用范围. 存取:能够保存数据避 ...

  4. SAS PROC MCMC example in R: Logistic Regression Random-Effects Model(转)

    In this post I will run SAS example Logistic Regression Random-Effects Model in four R based solutio ...

  5. css的定位,relative/absolute/fixed的用法

    其实position的值有四个,static/relative/absolute/fixed,而static是默认值,不算具有有定位属性,这里就不讲了. 定位其实就是跟元素设置定位属性,然后设置其对位 ...

  6. linux 系统备份日志

    题目: 备份日志 小明是一个服务器管理员,他需要每天备份论坛数据(这里我们用日志替代),备份当天的日志并删除之前的日志.而且备份之后文件名是年-月-日的格式.alternatives.log在/var ...

  7. DDD理论学习系列(5)-- 统一建模语言

    DDD理论学习系列--案例及目录 1.引言 上一节讲解了领域模型,领域模型主要是将业务中涉及到的概念以面向对象的思想进行抽象,抽象出实体对象,确定实体所对应的方法和属性,以及实体之间的关系.然后将这些 ...

  8. Expression 转化为sql(三) --自定义函数

    SQL 语句有很多函数如len(),now()等等.如何来生成这些函数.最近研究也写办法共大家参考. 一.首先建立一个建一个扩展类,控制只能允许这些函数出现,如果出现其他函数就直接报异常. publi ...

  9. DllImport 自动选择x64或x86 dll

    前言 标题不知道怎么确切地命名,在.net的托管世界里,有时不得不使用c的某个动态库,比如ocr.opencv等,如果幸运,有前人已经包装出.net版本,但有些不非常流行的库,只能自己使用pinvok ...

  10. POJ 1845-Sumdiv 题解(数论,约数和公式,逆元,高中数学)

    题目描述 给定A,B,求A^B的所有因数的和,再MOD 9901 输入 一行两个整数 A 和 B. 输出 一行,一个整数 样例输入 2 3 样例输出 15 提示 对于100%的数据满足:0 <= ...