第 11 章. Drawing Textures 绘制纹理

第 11 章. Drawing Textures 绘制纹理

在前一章中,我们学习了如何更新资源的内容并使用描述符在着色器阶段读取它们。 我们还介绍了 push 常量,它是在着色器阶段使用命令缓冲区更新常量数据的优化方式。 另外,通过使用描述符,我们对渲染的图元添加了 3D 变换,并且还演示了一个示例来学习 push 常量。

在本章中,我们会学习和实现纹理;我们会将它们缠绕在几何表面上,以便增强现实感。 纹理是使用 Vulkan 的图像资源创建的;其数据可以以线性或最佳布局存储。 我们将实现这两种布局 – 后者布局使用暂存。 在暂存中,物理的分配过程使用了两个不同的存储区域。 主机可能无法看到资源的理想内存布局。 在这种情况下,应用程序必须首先将资源填充到主机可见的暂存缓冲区中,然后将其转移到理想的位置。

在本章中,我们会介绍以下主题:

  • 图像资源 Image resource – 快速回顾
  • 纹理绘制 texture drawing 的先决条件
  • 用线性平铺 linear tiling 实现图像资源
  • 用优化平铺 optimal tiling 实现图像资源
  • 在图像和缓冲区之间复制数据内容
  • 更新描述符集 descriptor set

Image resource – a quick recap

图像资源 – 快速回顾

图像是以 1D,2D 或 3D 形式存储的连续字节数组。 与缓冲区资源不同,图像是存储在内存中格式化的信息。

Vulkan 中的图像资源由 VkImage 对象表示,并使用 vkCreateImage API 创建。 这个对象的创建还没有实际的图像内容作为后端支持。 这必须单独完成,其中需要分配设备内存并将图像内容存储到其中。 然后把该内存绑定到创建的对象。

为了在着色器阶段使用创建的图像对象,必须将它们转换为图像视图 -VkImageView。 在将图像转换为图像视图之前,必须使用图像布局使其与底层实现兼容。

使用 VkImageLayout 将图像转换为实现相关的布局。 对于给定的图像资源,可以创建并使用多个图像布局。 不同的布局可能会暴露不同的性能特征,因为它们非常专注于 usage 类型。 因此指明正确的用法 usage 可让驱动程序选择一个特定的存储位置或部分区域,适合用来提供最佳的性能。

如果您想了解图像资源的详细介绍,请参阅第 6 章“分配图像资源以及使用 WSI 创建交换链接”中的第一部分,即“图像资源入门”。 在同一章节中,您可以参考“了解图像资源”部分以获取有关图像,图像视图和图像布局的详细信息。

创建一个图像资源很简单,由以下步骤组成:

  1. 图像对象的创建:Image object creation,首先,创建 VkImage 对象。 此对象不包含图像数据,但它存储了图像资源各种重要的对象状态,例如格式,尺寸,图像类型,图像的 usage 类型,平铺样式等。 一个给定的图像可以具有多个子图像资源,例如 mipmap。 以下是创建图像对象的步骤:
    1. 平铺:Tiling,可以指定图像平铺的两种方式:线性和最佳。 在线性布局中,图像数据会被映射到设备上的连续内存,以线性方式排列。 然而,在最佳布局中,图像以贴片的形式存储,并且每个贴片内的纹理元素可以以线性或某种专有格式排列以提供最佳的性能。 有关线性和最佳布局的详细介绍,请参阅第 6 章“分配图像资源以及使用 WSI 创建交换链”中的“平铺简介”一节。
    2. 分配和赋值图像数据:Allocating and assigning image data,读取图像内容并将所需的内存分配给图像资源。 使用图像通道的内容填充分配的设备内存。
    3. 设置正确的布局:Setting the correct layout,创建一个实现兼容的图像布局。 单个图像及其子资源可以用多个布局来指定。

  2. 图像采样器:Image sampler,创建采样器(VkSampler)来控制纹理的过滤。
  3. 图像视图创建:Image view creation,图像资源只能在着色器中以图像视图(VkImageView)的形式访问。

Prerequisites for texture drawing

纹理绘制的先决条件

实现纹理很简单,只需要几个步骤。 我们首先快速浏览一下,然后我们会对其进行深入的探讨:

  1. 纹理坐标:Texture coordinates,使用纹理坐标将纹理粘贴到几何表面。 对于每个顶点,都附有相应的纹理坐标。 在我们的实现中,我们以交错的形式指定了顶点和纹理坐标。
  2. 着色器阶段:The shader stage,修改顶点和片段着色器以便约束纹理资源。 着色器阶段允许片段着色器访问纹理资源以及绘制片段。 在着色器阶段,纹理会以采样器的形式共享。
  3. 加载图像文件:Loading the image files,解析图像文件并将原始图像数据加载到本地的数据结构中。 这将有助于生成 Vulkan 图像资源并在着色器阶段共享它们。
  4. 本地图像数据结构:Local image data structure,TextureData 本地数据结构存储了所有图像特定的属性。

Specifying the texture coordinates 指定纹理坐标

几何坐标(x,y,z,w)与纹理坐标(u,v)以交错的形式定义在 MeshData.h 文件内的 VertexWithUV 结构中:

在本示例中,我们会渲染用纹理面绘制的立方体。 以下代码显示了立方体的一个面,具有四个顶点位置,后面跟着两个纹理坐标。 完整的代码请参考 MeshData.h:

Updating the shader program 更新着色器程序

除了顶点坐标之外,现在我们的顶点着色器也会考虑纹理坐标。 输入的纹理坐标在布局位置 1 的 inUV 属性中进行接收。 然后这些坐标会被传递到片段着色器阶段并被接收到 outUV 中。 以下代码以粗体显示在现有顶点着色器中所做的修改:

layout (location = 0) in vec4 pos;
layout (location = 1) in vec2 inUV; layout (location = 0) out vec2 outUV;
void main() {
outUV = inUV;
gl_Position = myBufferVals.mvp * pos; gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
}

以下代码实现了片段着色器,其中在布局绑定索引 1 处接收了采样过的纹理。接收到的纹理与输入的纹理坐标一起使用,以便获取片段的最终颜色:

layout (location = 0) in vec2 uv;
layout (location = 0) out vec4 outColor;

void main() {
outColor = texture(tex, uv);
}

Loading the image files 加载图像文件

图像文件使用 GLI 库加载到我们的示例应用程序中。 OpenGL Image(GLI)是仅只有头文件的 C ++ 图像库,支持为图形软件应用程序加载 KTX 和 DDS 图像文件。 它提供了各种功能,如纹理加载和创建,纹理压缩,纹理纹素的访问,样本纹理,纹理转换,mipmap 等。

你可以从 gli.g-truc.net/0.8.1/in 下载这个库。 为了使用这个库,请执行以下更改: – CMakeLists.txt:通过将以下行添加到项目的 CMakeLists.txt 文件来增加 GLI 支持:

  • 头文件:需要在 Headers.h 文件中包括 GLI 的头文件:

Using the GLI library 使用 GLI 库

以下代码是我们应用程序中 GLI 库的最小用法。 此代码演示了图像加载,尺寸查询,mipmap 级别以及图像数据的检索:

// Get the image dimensions at ith sub- resource uint32_t textureWidth = image2D[i].dimensions().x; uint32_t textureHeight = image2D[i].dimensions().y;

// Get number of mip- map levels
uint32_t mipMapLevels = image2D.levels();

// Retrieve the raw image data
void* rawData = image2D.data();

Local image data structure 本地图像数据结构

wrapper.h 包含一个用户定义的 TextureData 结构,用于保存应用程序中的图像属性以及各种特定于图像的信息。 以下是每个字段的语法和描述:

下表描述了用户定义结构 TextureData 的各个字段:

字段 | 描述

—|—

sampler | 这是与图像对象关联的 VkSampler 对象。

image | 这是 VkImage 对象。

imageLayout | 这个字段包含图像资源对象特定的依赖实现的布局信息。

memoryAlloc | 这个字段存储了与关联的图像对象(VkImage)绑定的内存分配信息。 mem | 这是指为此图像资源分配的物理设备内存。

view | 这是图像 image 的 ImageView 对象。

mipMapLevels | 这是指图像资源中的 mipmap 级别的数量。 layerCount | 这是指图像资源中层计数的数量。 textureWidth
textureHeight | 这些是图像资源的尺寸。

descsImgInfo | 这个字段是描述符图像信息,其中包含使用适当图像布局 usage 类型的图像视图和样本信息。

在下一节中,我们就开始实现图像资源并将其发挥作用。

Implementing the image resource with linear tiling

用线性平铺实现图像资源

在本节中,我们将使用线性平铺实现图像资源,并在我们上一章中实现的渲染立方体的表面上显示纹理图像。

正如我们在第 6 章“分配图像资源以及使用 WSI 构建交换链”中的“平铺简介”一节中所了解的那样,有两种类型的图像平铺 – 线性和最佳:

  • 线性平铺:Linear tiling,在这种类型的平铺排列中,图像纹素以逐行的方式(行主序)排列,这可能需要一些填充以匹配行间距。 行间距定义了行的宽度;因此,如果排列的纹理元素行小于行间距,则需要使用填充。 VkImageCreateInfo 通过 tiling 字段(VkImageTiling 类型)指示线性平铺。 该参数必须指定为 VK_IMAGE_TILING_LINEAR。
  • 最佳平铺:Optimal tiling,顾名思义,图像的纹理元素以特定于实现的方式进行排列,旨在通过优化内存访问来提供更好的性能。 这里,tiling 字段必须指定为 VK_IMAGE_TILING_OPTIMAL。

线性图像平铺在 VulkanRenderer 类的 createTextureLinear()函数中实现。 函数需要四个参数。 第一个参数(filename)指定要加载哪个图像文件。 第二个参数是一个 TextureData 数据结构,应该在其中存储创建的图像和属性。 第三个参数 imageUsageFlags 指示图像资源的提示,指定使用它的目的。 最后一个参数 format 指定创建图像对象必须使用的图像格式。 这是它的语法:

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年7月16日
下一篇 2019年7月16日

相关推荐