[译]Vulkan教程(29)组合的Image采样器
[译]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, 0到1, 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采样器的更多相关文章
- [译]Vulkan教程(28)Image视图和采样器
[译]Vulkan教程(28)Image视图和采样器 Image view and sampler - Image视图和采样器 In this chapter we're going to creat ...
- [译]Vulkan教程(27)Image
[译]Vulkan教程(27)Image Images Introduction 入门 The geometry has been colored using per-vertex colors so ...
- [译]Vulkan教程(14)图形管道基础之固定功能
[译]Vulkan教程(14)图形管道基础之固定功能 Fixed functions 固定功能 The older graphics APIs provided default state for m ...
- [译]Vulkan教程(32)生成mipmap
[译]Vulkan教程(32)生成mipmap Generating Mipmaps 生成mipmap Introduction 入门 Our program can now load and ren ...
- [译]Vulkan教程(31)加载模型
[译]Vulkan教程(31)加载模型 Loading models 加载模型 Introduction 入门 Your program is now ready to render textured ...
- [译]Vulkan教程(30)深度缓存
[译]Vulkan教程(30)深度缓存 Depth buffering 深度缓存 Introduction 入门 The geometry we've worked with so far is pr ...
- [译]Vulkan教程(25)描述符布局和buffer
[译]Vulkan教程(25)描述符布局和buffer Descriptor layout and buffer 描述符布局和buffer Introduction 入门 We're now able ...
- [译]Vulkan教程(21)顶点input描述
[译]Vulkan教程(21)顶点input描述 Vertex input description 顶点input描述 Introduction 入门 In the next few chapters ...
- [译]Vulkan教程(16)图形管道基础之总结
[译]Vulkan教程(16)图形管道基础之总结 Conclusion 总结 We can now combine all of the structures and objects from the ...
随机推荐
- 【Web技术】281- 滴滴开源小程序框架 Mpx2.0
滴滴Mpx框架负责人@hiyuki,滴滴出行网约车webapp乘客团队的负责人,也是滴滴开源的小程序框架Mpx的负责人和核心作者 Mpx是一款致力于提高小程序开发体验和效率的增强型小程序框架,目前在滴 ...
- Orleans[NET Core 3.1] 学习笔记(一).NET环境下的分布式应用程序
前言 Orleans是一个跨平台的框架,用于搭建可扩展的分布式应用程序 第一次接触Orleans还是两年前做游戏服务器的时候,用SignalR+Orleans的组合,写起代码来不要太爽. 即将进入20 ...
- wxxcx_learn异常处理
属于基础框架,分级别 捕获异常,处理异常(记录日志,修复异常,返回客户端错误),抛出异常 全局异常处理(AOP)对错误同一格式化 try{ $banner = BannerModel::getBann ...
- skipping archived logs of thread 1 from sequence 29 to 46; already backed up
问题描述:删除归档的备份,在进行归档的重新备份,提示:skipping archived logs of thread 1 from sequence 29 to 46; already backed ...
- 1. 初探--prometheus调研
一.基本概念 Prometheus 是由前 Google 工程师从 2012 年开始在 Soundcloud 以开源软件的形式进行研发的系统监控和告警工具包,自此以后,许多公司和组织都采用了 Prom ...
- webpack学习2.3webpack核心概念
核心概念(四个) Entry(入口) Output(出口) Loaders()来处理其他类型的资源文件 Plugins(插件) 1.入口(Entry) 作用:代码的入口,打包的入口,单个或多个, 示例 ...
- django基础之day09,Forms组件在程序中做了哪些事? 校验数据、渲染标签、展示信息
******************************* Forms组件 *************************************************** Forms组件在 ...
- 慢sql查询优化
explain使用介绍 id:执行编号,标识select所属的行.如果在语句中没子查询或关联查询,只有唯一的select,每行都将显示1.否则,内层的select语句一般会顺序编号,对应于其在原始语句 ...
- 在WPF中开启摄像头扫描二维码(Media+Zxing)
近两天项目中需要添加一个功能,是根据摄像头来读取二维码信息,然后根据读出来的信息来和数据库中进行对比显示数据. 选择技术Zxing.WPFMediaKit.基本的原理就是让WPFmediaKit来对摄 ...
- django 用户与权限管理
django中使用content_type表进行存储app与model的关系.在permission表中设立了name(权限的名字,中英文均可)content_type_id(与content_typ ...