[译]Vulkan教程(29)组合的Image采样器

Combined image sampler 组合的image采样器

Introduction 入门

We looked at descriptors for the first time in the uniform buffers part of the tutorial. In this chapter we will look at a new type of descriptor: combined image sampler. This descriptor makes it possible for shaders to access an image resource through a sampler object like the one we created in the previous chapter.

在本教程的uniform buffer部分,我们首次认识了描述符。本章,我们将认识一个新类型的描述符:组合的image采样器(译者注:后文简称CIS)。这个描述符让shader通过采样器对象(例如我们在上一章创建的那个)存取image资源成为可能。

We'll start by modifying the descriptor layout, descriptor pool and descriptor set to include such a combined image sampler descriptor. After that, we're going to add texture coordinates to Vertex and modify the fragment shader to read colors from the texture instead of just interpolating the vertex colors.

开始,我们修改描述符布局,描述符池和描述符set,以包含这样一个CIS描述符。之后,我们要添加纹理坐标到Vertex ,修改Fragment shader来从纹理读取颜色,而不是从顶点颜色插值。

Updating the descriptors 更新描述符

Browse to the createDescriptorSetLayout function and add a VkDescriptorSetLayoutBinding for a combined image sampler descriptor. We'll simply put it in the binding after the uniform buffer:

找到createDescriptorSetLayout 函数,为CIS描述符添加一个VkDescriptorSetLayoutBinding 。我们简单地将其放到uniform buffer后面的binding:

VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding, samplerLayoutBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();

Make sure to set the stageFlags to indicate that we intend to use the combined image sampler descriptor in the fragment shader. That's where the color of the fragment is going to be determined. It is possible to use texture sampling in the vertex shader, for example to dynamically deform a grid of vertices by a heightmap.

确保设置stageFlags  to表示我们想在Fragment shader中用CIS描述符。那是决定Fragment颜色的地方。可以在顶点shader中用纹理采样,例如用于使顶点网格动态变形by heightmap

If you would run the application with validation layers now, then you'll see that it complains that the descriptor pool cannot allocate descriptor sets with this layout, because it doesn't have any combined image sampler descriptors. Go to the createDescriptorPool function and modify it to include a VkDescriptorPoolSize for this descriptor:

如果你现在启用验证层运行程序,那么你会看到它抱怨说描述符池不能分配这个布局下的描述符set,因为它没有任何CIS描述符。找到createDescriptorPool 函数,修改它to包含VkDescriptorPoolSize for这个描述符:

std::array<VkDescriptorPoolSize, 2> poolSizes = {};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size()); VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());

The final step is to bind the actual image and sampler resources to the descriptors in the descriptor set. Go to the createDescriptorSets function.

最后一步是,绑定image和采样器资源到描述符set中的描述符。找到createDescriptorSets 函数。

for (size_t i = 0; i < swapChainImages.size(); i++) {
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject); VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = textureImageView;
imageInfo.sampler = textureSampler; ...
}

The resources for a combined image sampler structure must be specified in a VkDescriptorImageInfo struct, just like the buffer resource for a uniform buffer descriptor is specified in a VkDescriptorBufferInfo struct. This is where the objects from the previous chapter come together.

CIS结构体的资源必须在VkDescriptorImageInfo 结构体中指定,就像buffer资源for uniform buffer描述符在VkDescriptorBufferInfo 结构体中指定一样。这就与上一章的对象联系起来了。

std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};

descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSets[i];
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo; descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = descriptorSets[i];
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pImageInfo = &imageInfo; vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);

The descriptors must be updated with this image info, just like the buffer. This time we're using the pImageInfo array instead of pBufferInfo. The descriptors are now ready to be used by the shaders!

描述符必须用这个image信息更新,就像buffer那样。这次我们要用pImageInfo数组,而不是pBufferInfo。描述符现在准备好被shader使用了!

Texture coordinates 纹理坐标

There is one important ingredient for texture mapping that is still missing, and that's the actual coordinates for each vertex. The coordinates determine how the image is actually mapped to the geometry.

还有一个元素for纹理映射that没有提到,那就是每个顶点的纹理坐标。这个坐标决定了image如何映射到几何体上。

struct Vertex {
glm::vec2 pos;
glm::vec3 color;
glm::vec2 texCoord; static VkVertexInputBindingDescription getBindingDescription() {
VkVertexInputBindingDescription bindingDescription = {};
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return bindingDescription;
} static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {}; attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vertex, pos); attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vertex, color); attributeDescriptions[2].binding = 0;
attributeDescriptions[2].location = 2;
attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[2].offset = offsetof(Vertex, texCoord); return attributeDescriptions;
}
};

  

Modify the Vertex struct to include a vec2 for texture coordinates. Make sure to also add a VkVertexInputAttributeDescription so that we can use access texture coordinates as input in the vertex shader. That is necessary to be able to pass them to the fragment shader for interpolation across the surface of the square.

修改Vertex 结构体to包含一个vec2  for纹理坐标。确保也添加一个VkVertexInputAttributeDescription ,这样我们可以用纹理坐标作为顶点shader的输入。这对于传递它们到Fragment shader后在四边形上插值是必要的。

const std::vector<Vertex> vertices = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
};

  

In this tutorial, I will simply fill the square with the texture by using coordinates from 0, 0 in the top-left corner to 1, 1 in the bottom-right corner. Feel free to experiment with different coordinates. Try using coordinates below 0 or above 1 to see the addressing modes in action!

本教程中,我们将简单地填入四边形with这个纹理by使用坐标-从左上角的0, 0到右下角的1, 1。你可以随意体验不同的坐标。尝试使用低于0或高于1的坐标to实际看看取址模式!

Shaders

The final step is modifying the shaders to sample colors from the texture. We first need to modify the vertex shader to pass through the texture coordinates to the fragment shader:

最后一步是修改shader来从纹理中采样颜色。我们首先需要修改顶点shader来传入纹理坐标到Fragment shader:

layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord; layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord; void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}

  

Just like the per vertex colors, the fragTexCoord values will be smoothly interpolated across the area of the square by the rasterizer. We can visualize this by having the fragment shader output the texture coordinates as colors:

就像逐顶点的颜色一样,fragTexCoord 值会在四边形上被光栅器平滑地插值。我们可以看到这个by让Fragment shader输出纹理坐标为颜色:

#version 450
#extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; void main() {
outColor = vec4(fragTexCoord, 0.0, 1.0);
}

  

You should see something like the image below. Don't forget to recompile the shaders!

你会看到下图所示的内容。别忘记重编译shader!

The green channel represents the horizontal coordinates and the red channel the vertical coordinates. The black and yellow corners confirm that the texture coordinates are correctly interpolated from 0, 0 to 1, 1 across the square. Visualizing data using colors is the shader programming equivalent of printf debugging, for lack of a better option!

绿色通道代表水平坐标,红色通道代表竖直坐标。黑色和黄色的角落确认了that纹理坐标被正确得在四边形上从0, 01, 1插值了。彩色可视化数据是shader编程中等价于printf 的调试方式,如果没有更好的选择!

A combined image sampler descriptor is represented in GLSL by a sampler uniform. Add a reference to it in the fragment shader:

一个CIS描述符在GLSL中用一个采样器uniform变量代表。在Fragment shader中添加对它的引用:

layout(binding = 1) uniform sampler2D texSampler;

  

There are equivalent sampler1D and sampler3D types for other types of images. Make sure to use the correct binding here.

对其他类型的image,有等价的sampler1D 和sampler3D 。确保这里使用正确的绑定。

void main() {
outColor = texture(texSampler, fragTexCoord);
}

  

Textures are sampled using the built-in texture function. It takes a sampler and coordinate as arguments. The sampler automatically takes care of the filtering and transformations in the background. You should now see the texture on the square when you run the application:

纹理通过内置的texture 函数进行采样。它接收一个sampler 和坐标作为参数。采样器自动在后台处理过滤和变换问题。你现在可以看到纹理在四边形上when你运行程序:

Try experimenting with the addressing modes by scaling the texture coordinates to values higher than 1. For example, the following fragment shader produces the result in the image below when using VK_SAMPLER_ADDRESS_MODE_REPEAT:

尝试试验取址模式by缩放纹理坐标到大于1的值。例如,下述Fragment shader会产生下述image-when使用VK_SAMPLER_ADDRESS_MODE_REPEAT模式:

void main() {
outColor = texture(texSampler, fragTexCoord * 2.0);
}

You can also manipulate the texture colors using the vertex colors:

你也可以修改纹理颜色-使用顶点颜色:

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

I've separated the RGB and alpha channels here to not scale the alpha channel.

我将RGB和alhpa通道分开了to不缩放alpha通道。

You now know how to access images in shaders! This is a very powerful technique when combined with images that are also written to in framebuffers. You can use these images as inputs to implement cool effects like post-processing and camera displays within the 3D world.

现在你指定如何在shader中读取image了!这是个非常强大的技术whne结合写入帧缓存的image。你可以用这些image作为输入来实现很酷的效果-例如后处理和3D世界的摄像机显示。

C++ code / Vertex shader / Fragment shader

[译]Vulkan教程(29)组合的Image采样器的更多相关文章

  1. [译]Vulkan教程(28)Image视图和采样器

    [译]Vulkan教程(28)Image视图和采样器 Image view and sampler - Image视图和采样器 In this chapter we're going to creat ...

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

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

  3. [译]Vulkan教程(14)图形管道基础之固定功能

    [译]Vulkan教程(14)图形管道基础之固定功能 Fixed functions 固定功能 The older graphics APIs provided default state for m ...

  4. [译]Vulkan教程(32)生成mipmap

    [译]Vulkan教程(32)生成mipmap Generating Mipmaps 生成mipmap Introduction 入门 Our program can now load and ren ...

  5. [译]Vulkan教程(31)加载模型

    [译]Vulkan教程(31)加载模型 Loading models 加载模型 Introduction 入门 Your program is now ready to render textured ...

  6. [译]Vulkan教程(30)深度缓存

    [译]Vulkan教程(30)深度缓存 Depth buffering 深度缓存 Introduction 入门 The geometry we've worked with so far is pr ...

  7. [译]Vulkan教程(25)描述符布局和buffer

    [译]Vulkan教程(25)描述符布局和buffer Descriptor layout and buffer 描述符布局和buffer Introduction 入门 We're now able ...

  8. [译]Vulkan教程(21)顶点input描述

    [译]Vulkan教程(21)顶点input描述 Vertex input description 顶点input描述 Introduction 入门 In the next few chapters ...

  9. [译]Vulkan教程(16)图形管道基础之总结

    [译]Vulkan教程(16)图形管道基础之总结 Conclusion 总结 We can now combine all of the structures and objects from the ...

随机推荐

  1. Web 前端学习大纲

    什么是前端? 前端即网站前台部分,也叫前端开发,运行在PC端,移动端等浏览器上展现给用户浏览的网页.随着互联网的发展,HTML5,CSS3,前端框架的应用,跨平台响应式网页设计能够适应各种屏幕分辨率, ...

  2. .NET Core RSA 指南与增强扩展 RSAExtensions

    一. 前言 RSA 作为最常用的非对称加密算法,在我们的实际使用中还是比较常见的,特别是对接支付十有八九都会遇到,或者是其他需要数据安全的业务场景.在 .NET Framework 以及 .NET C ...

  3. 基于 asm 实现比 spring BeanUtils 性能更好的属性拷贝框架

    Bean-Mapping 日常开发中经常需要将一个对象的属性,赋值到另一个对象中. 常见的工具有很多,但都多少不够简洁,要么不够强大. 我们经常使用的 Spring BeanUtils 性能较好,但是 ...

  4. 反射从入门到精通之深入了解Class类

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  5. Mybatis-plus入门教程(一)

    什么是MyBatis-Plus MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发繁琐的CRUD.提高代码效率而生,同时 ...

  6. textarea增加字数监听且高度自适应(兼容IE8)

    1.封装方法: var textareaListener = { /*事件监听器兼容 * *attachEvent——兼容:IE7.IE8:不兼容firefox.chrome.IE9.IE10.IE1 ...

  7. 网络配置工具iproute2和net-tools的基本原理和基本使用方法

    这是网络程序设计课程的第一次作业的博客,由于还是小白,分享的内容都是比较基础的东西,希望看到的各位同学可以提出指导意见,必将虚心听取. 这次分享的内容是网络配置工具iproute2和net-tools ...

  8. python基础知识第六篇(知识点总结)

    ####################### 整理 ################# # 一.数字# int(..) # 二.字符串# replace(替换)/find/join/strip(移除 ...

  9. Java 打印Word文档

    本文介绍如何在Java程序中通过物理打印机和虚拟打印机来打印Word文档的方法.文中使用了类库Spire.Doc for Java,可通过官网下载jar文件并导入程序或者直接通过maven仓库安装导入 ...

  10. WinForm WebBrowser 设置cookie

    [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] public static exte ...