纹理贴图
若希望绘制出更加真实、酷炫的3D场景,就需要用到纹理映射。其中主要包括纹理映射的基本原理、4种不同的拉伸方式、两种不同的采样方式、Mipmap纹理、多重纹理与过程纹理、压缩纹理、点精灵、3D纹理和2D纹理数组等
纹理映射的基本思想就是首先为图元中的每个顶点指定恰当的纹理坐标,然后通过纹理坐标在纹理图中可以确定选中的纹理区域,最后将选中纹理区域中的内容根据纹理坐标映射到指定的图元上。
各个片元着色的颜色需要根据片元的纹理坐标从纹理图中提取,具体过程如下。
首先图元中的每个顶点都需要在顶点着色器中通过out变量将接收的纹理坐标传入片元着色器。
然后经过顶点着色器时,其中渲染管线的部分固定功能会根据情况进行插值计算,产生对应到每个片元的用于记录纹理坐标的out变量值。
最后每个片元在片元着色器中根据其接收到的记录纹理坐标的in变量值到纹理中提取出对应位置的颜色即可,提取颜色的过程一般称之为纹理采样。
一个是用于存储纹理数据的TexDataObject类,另一个是用于执行各项纹理相关任务的TextureManager类。
TexDataObject是本章新增的用于存储纹理数据及相关信息的类,此类对象中可以保存所加载纹理的宽度、高度、数据总字节数、纹理数据等
TexDataObject.h
#ifndef VULKANEXBASE_TEXDATAOBJECT_H
#define VULKANEXBASE_TEXDATAOBJECT_H//防止被重复引用
class TexDataObject {
public:
int width;//纹理宽度
int height;//纹理高度
int dataByteCount;//纹理的数据总字节数
unsigned char* data;//指向纹理数据存储内存首地址的指针
TexDataObject(int width, int height, unsigned char* data, int dataByteCount);//构造函数
~TexDataObject();//析构函数
};
#endif
只是包含了几个用于存储各项信息的成员变量。另外,此类通用于所有的2D纹理,包括bntex格式的非压缩纹理和其他压缩格式(如ETC2、BC3等)的纹理。
TextureManager是本章新增的用于执行各项纹理相关任务的类,其中包含了所需的一些成员变量及功能方法。这些成员变量主要包括:纹理图像列表、纹理图像内存列表、纹理图像视图列表等。功能方法主要包括:加载纹理的方法、销毁纹理的方法、初始化采样器的方法等。
#ifndef VULKANEXBASE_TEXTUREMANAGER_H
#define VULKANEXBASE_TEXTUREMANAGER_H
#include <vector>
#include <map>
#include <string>
#include <vulkan/vulkan.h>
#include "TexDataObject.h"
#define SAMPLER_COUNT 1 //采样器数量
class TextureManager {
public:
static std::vector<std::string> texNames; //纹理文件名称列表
static std::vector<VkSampler> samplerList; //采样器列表
static std::map<std::string, VkImage> textureImageList; //纹理图像列表
static std::map<std::string, VkDeviceMemory> textureMemoryList; //纹理图像内存列表
static std::map<std::string, VkImageView> viewTextureList; //纹理图像视图列表
static std::map<std::string, VkDescriptorImageInfo> texImageInfoList; //纹理图像描述信息列表
static void initTextures(VkDevice& device, VkPhysicalDevice& gpu,
VkPhysicalDeviceMemoryProperties& memoryroperties,
VkCommandBuffer& cmdBuffer, VkQueue& queueGraphics); //加载所有纹理的方法
static void deatroyTextures(VkDevice& device); //销毁所有纹理的方法
static int getVkDescriptorSetIndex(std::string texName);//获取指定名称纹理在描述集列表中的索引
private:
static void initSampler(VkDevice& device, VkPhysicalDevice& gpu); //初始化采样器的方法
static void init_SPEC_2D_Textures(std::string texName, VkDevice& device, VkPhysicalDevice& gpu,
VkPhysicalDeviceMemoryProperties& memoryroperties, VkCommandBuffer& cmdBuffer,
VkQueue& queueGraphics, VkFormat format, TexDataObject* ctdo); //加载2D纹理的方法
};
#endif
//初始化采样器的方法
void TextureManager::initSampler(VkDevice& device, VkPhysicalDevice& gpu) {
//构建了采样器创建信息结构体实例,设置了很多关于采样器的参数,包括纹理采样方式、拉伸方式、Mipmap相关参数、各向异性过滤相关参数等。
VkSamplerCreateInfo samplerCreateInfo = {}; //构建采样器创建信息结构体实例
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; //结构体的类型
samplerCreateInfo.magFilter = VK_FILTER_LINEAR; //放大时的纹理采样方式
samplerCreateInfo.minFilter = VK_FILTER_NEAREST; //缩小时的纹理采样方式
//initSampler方法的代码中创建的采样器主要是服务于非Mipmap的一般纹理,故设置的Mipmap模式并没有实际作用
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;//mipmap 模式
samplerCreateInfo.addressModeU =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; //纹理 S 轴的拉伸方式
samplerCreateInfo.addressModeV =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; //纹理 T 轴的拉伸方式
samplerCreateInfo.addressModeW =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; //纹理 W 轴的拉伸方式
samplerCreateInfo.mipLodBias = 0.0; //mipmap 时的 Lod 调整值
samplerCreateInfo.anisotropyEnable = VK_FALSE; //是否启用各向异性过滤
samplerCreateInfo.maxAnisotropy = 1; //各向异性最大过滤值
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; //纹素数据比较操作
//的最大/最小LOD(细节级别,Levels of Detail)值都设置为0,表示对应的纹理没有采用Mipmap。
samplerCreateInfo.minLod = 0.0; //最小 LOD 值
samplerCreateInfo.maxLod = 0.0; //最大 LOD 值
samplerCreateInfo.compareEnable = VK_FALSE; //是否开启比较功能
samplerCreateInfo.borderColor =
VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; //要使用的预定义边框颜色
//创建了指定数量的采样器,并将采样器添加进列表。由于本案例比较简单,故只有一个采样器,但复杂的情况下会有多个采样器同时出现。
for (int i = 0; i<SAMPLER_COUNT; i++) { //循环创建指定数量的采样器
VkSampler samplerTexture; //声明采样器对象
VkResult result = vkCreateSampler(device,
&samplerCreateInfo, NULL, &samplerTexture); //创建采样器
samplerList.push_back(samplerTexture); //将采样器加入列表
}
}
加载2D纹理的方法—init_SPEC_2D_Textures,其不但能用于加载bntex格式的非压缩纹理,还能用于加载其他压缩格式(如:ETC2、BC3等)的2D纹理
void TextureManager::init_SPEC_2D_Textures(std::string texName, VkDevice& device, VkPhysicalDevice& gpu, VkPhysicalDeviceMemoryProperties& memoryroperties, VkCommandBuffer& cmdBuffer, VkQueue& queueGraphics, VkFormat format, TexDataObject* ctdo)
{
VkFormatProperties formatProps;//指定格式纹理的格式属性
//首先通过vkGetPhysicalDeviceFormatProperties方法获取指定物理设备中VK_FORMAT_R8G8B8A8_UNORM格式的属性,然后基于获取的格式属性判断此格式纹理是否能够采用线性瓦片,并将判断结果存储在布尔型变量needStaging中。
vkGetPhysicalDeviceFormatProperties(gpu, format, &formatProps);//获取指定格式纹理的格式属性
//行根据是否能够采用线性瓦片分两种情况进行处理
bool needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; //判断是否能使用线性瓦片纹理
printf("TextureManager %s", (needStaging ? "不能使用线性瓦片纹理" : "能使用线性瓦片纹理"));
if (needStaging)
{
//构建了缓冲创建信息结构体实例,进而设置此结构体实例的多项属性。主要包括结构体类型、缓冲的用途、数据总字节数、队列家族数量、队列家族索引列表、共享模式等,然后创建了缓冲,最后检查缓冲创建是否成功。
VkBuffer tempBuf; //中转存储用的缓冲
VkBufferCreateInfo buf_info = {}; //构建缓冲创建信息结构体实例
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; //设置结构体类型
buf_info.pNext = NULL; //自定义数据的指针
buf_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; //缓冲的用途为传输源
buf_info.size = ctdo->dataByteCount; //数据总字节数
buf_info.queueFamilyIndexCount = 0; //队列家族数量
buf_info.pQueueFamilyIndices = NULL; //队列家族索引列表
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; //共享模式
buf_info.flags = 0; //标志
VkResult result = vkCreateBuffer(device, &buf_info, NULL, &tempBuf); //创建缓冲
assert(result == VK_SUCCESS); //检查缓冲创建是否成功
VkMemoryRequirements mem_reqs; //缓冲的内存需求
//先根据创建的缓冲通过vkGetBufferMemoryRequirements方法获取了缓冲的内存需求,进而从获取的内存需求中得到所需内存的字节数,然后构建内存分配信息结构体实例,设置其内存字节数属性,并根据给定的内存类型掩码获取了所需的内存类型索引,以备分配内存时使用.
vkGetBufferMemoryRequirements(device, tempBuf, &mem_reqs); //获取缓冲内存需求
assert(ctdo->dataByteCount <= mem_reqs.size); //检查内存需求获取是否正确
VkMemoryAllocateInfo alloc_info = {}; //构建内存分配信息结构体实例
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; //结构体类型
alloc_info.pNext = NULL; //自定义数据的指针
alloc_info.memoryTypeIndex = 0; //内存类型索引
alloc_info.allocationSize = mem_reqs.size; //内存总字节数
VkFlags requirements_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; //需要的内存类型掩码
bool flag = memoryTypeFromProperties(memoryroperties, mem_reqs.memoryTypeBits,
requirements_mask, &alloc_info.memoryTypeIndex); //获取所需内存类型索引
if (flag) {
printf("确定内存类型成功 类型索引为%d", alloc_info.memoryTypeIndex);
}
else { printf("确定内存类型失败!"); }
VkDeviceMemory memTemp; //设备内存
//通过vkAllocateMemory方法基于前面构建的内存分配信息结构体实例分配了设备内存,并检查内存分配是否成
result = vkAllocateMemory(device, &alloc_info, NULL, &memTemp); //分配设备内存
assert(result == VK_SUCCESS); //检查内存分配是否成功
uint8_t *pData; //CPU 访问时的辅助指针
result = vkMapMemory(device, memTemp,
0, mem_reqs.size, 0, (void **)&pData); //将设备内存映射为 CPU 可访问
assert(result == VK_SUCCESS); //检查映射是否成功
memcpy(pData, ctdo->data, ctdo->dataByteCount); //将纹理数据拷贝进设备内存
vkUnmapMemory(device, memTemp); //解除内存映射
result = vkBindBufferMemory(device, tempBuf, memTemp, 0);//绑定内存与缓冲
assert(result == VK_SUCCESS); //检查绑定是否成功
//构建了图像创建信息结构体实例,并设置图像创建信息结构体实例的多项属性,主要包括图像类型、图像像素格式、 图像尺寸、Mipmap 级数、数组层数量、采样模式、用途、共享模式、队列家族相关信息等。
VkImageCreateInfo image_create_info = {}; //构建图像创建信息结构体实例
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; //结构体的类型
image_create_info.pNext = NULL; //自定义数据的指针
image_create_info.imageType = VK_IMAGE_TYPE_2D; //图像类型
image_create_info.format = format; //图像像素格式
image_create_info.extent.width = ctdo->width; //图像宽度
image_create_info.extent.height = ctdo->height; //图像高度
image_create_info.extent.depth = 1; //图像深度
image_create_info.mipLevels = 1; //图像 mipmap 级数
image_create_info.arrayLayers = 1; //图像数组层数量
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; //采样模式
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; //采用最优瓦片组织方式
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //初始布局
image_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | //图像用途
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
image_create_info.queueFamilyIndexCount = 0; //队列家族数量
image_create_info.pQueueFamilyIndices = NULL; //队列家族索引列表
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; //共享模式
image_create_info.flags = 0; //标志
//基于图像创建信息结构体实例创建了图像,然后检查创建是否成功,接着根据纹理名称将图像添加到纹理图像列表中。
VkImage textureImage; //纹理对应的图像
result = vkCreateImage(device, &image_create_info, NULL, &textureImage);//创建图像
assert(result == VK_SUCCESS); //检查图像创建是否成功
textureImageList[texName] = textureImage; //添加到纹理图像列表
//首先构建了内存分配信息结构体实例,然后设置其多项属性。其中内存字节数属性值来自于vkGetImageMemoryRequirements方法获取的纹理图像内存需求,内存类型索引通过memoryTypeFromProperties方法获得。
VkMemoryAllocateInfo mem_alloc = {}; //构建内存分配信息结构体实例
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; //结构体的类型
mem_alloc.pNext = NULL; //自定义数据的指针
mem_alloc.allocationSize = 0; //内存总字节数
mem_alloc.memoryTypeIndex = 0; //内存类型索引
vkGetImageMemoryRequirements(device, textureImage, &mem_reqs);//获取纹理图像内存需求
mem_alloc.allocationSize = mem_reqs.size; //实际分配的内存字节数
flag = memoryTypeFromProperties(memoryroperties, mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&mem_alloc.memoryTypeIndex); //获取内存类型索引
VkDeviceMemory textureMemory; //纹理图像对应设备内存
//先通过vkAllocateMemory方法分配了设备内存,然后将图像与分配的设备内存进行了绑定,最后将纹理数据复制进设备内存
result = vkAllocateMemory(device, &mem_alloc, NULL, &(textureMemory)); //分配设备内存
textureMemoryList[texName] = textureMemory; //添加到纹理内存列表
result = vkBindImageMemory(device, textureImage, textureMemory, 0);//将图像和设备内存绑定
VkBufferImageCopy bufferCopyRegion = {}; //构建缓冲图像拷贝结构体实例
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;//使用方面
bufferCopyRegion.imageSubresource.mipLevel = 0; // mipmap 级别
bufferCopyRegion.imageSubresource.baseArrayLayer = 0; //基础数组层
bufferCopyRegion.imageSubresource.layerCount = 1; //数组层的数量
bufferCopyRegion.imageExtent.width = ctdo->width; //图像宽度
bufferCopyRegion.imageExtent.height = ctdo->height; //图像高度
bufferCopyRegion.imageExtent.depth = 1; //图像深度
bufferCopyRegion.bufferOffset = 0; //偏移量
VkCommandBufferBeginInfo cmd_buf_info = {}; //构建命令缓冲启动信息结构体实例
cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;//结构体类型
cmd_buf_info.pNext = NULL; //自定义数据的指针
cmd_buf_info.flags = 0; //标志
cmd_buf_info.pInheritanceInfo = NULL; //继承信息
const VkCommandBuffer cmd_bufs[] = { cmdBuffer }; //命令缓冲数组
VkSubmitInfo submit_info[1] = {}; //提交信息数组
submit_info[0].pNext = NULL; //自定义数据的指针
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; //结构体类型
submit_info[0].waitSemaphoreCount = 0; //等待的信号量数量
submit_info[0].pWaitSemaphores = VK_NULL_HANDLE; //等待的信号量列表
submit_info[0].pWaitDstStageMask = VK_NULL_HANDLE; //给定目标管线阶段
submit_info[0].commandBufferCount = 1; //命令缓冲的数量
submit_info[0].pCommandBuffers = cmd_bufs; //命令缓冲列表
submit_info[0].signalSemaphoreCount = 0; //任务完毕后设置的信号量数量
submit_info[0].pSignalSemaphores = NULL; //任务完毕后设置的信号量数组
VkFenceCreateInfo fenceInfo; //栅栏创建信息结构体实例
VkFence copyFence; //拷贝任务用栅栏
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; //结构体类型
fenceInfo.pNext = NULL; //自定义数据的指针
fenceInfo.flags = 0; //供将来使用的标志位
vkCreateFence(device, &fenceInfo, NULL, ©Fence); //创建栅栏
vkResetCommandBuffer(cmdBuffer, 0); //清除命令缓冲
result = vkBeginCommandBuffer(cmdBuffer, &cmd_buf_info);//启动命令缓冲(开始记录命令)
setImageLayout(cmdBuffer, textureImage, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); //修改图像布局(为拷贝做准备)
vkCmdCopyBufferToImage(cmdBuffer, tempBuf, //将缓冲中的数据拷贝到纹理图像中
textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
setImageLayout(cmdBuffer, textureImage, //修改图像布局(为纹理采样准备)
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
result = vkEndCommandBuffer(cmdBuffer); //结束命令缓冲(停止记录命令)
result = vkQueueSubmit(queueGraphics, 1, submit_info, copyFence);//提交给队列执行
do { //循环等待执行完毕
result = vkWaitForFences(device, 1, ©Fence, VK_TRUE, 100000000);
} while (result == VK_TIMEOUT);
vkDestroyBuffer(device, tempBuf, NULL); //销毁中转缓冲
vkFreeMemory(device, memTemp, NULL); //释放中转缓冲的设备内存
vkDestroyFence(device, copyFence, NULL); //销毁拷贝任务用栅栏
}
else
{
VkImageCreateInfo image_create_info = {}; //构建图像创建信息结构体实例
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; //结构体的类型
image_create_info.pNext = NULL; //指向自定义数据的指针
image_create_info.imageType = VK_IMAGE_TYPE_2D; //图像类型
image_create_info.format = format; //图像像素格式
image_create_info.extent.width = ctdo->width; //图像宽度
image_create_info.extent.height = ctdo->height; //图像高度
image_create_info.extent.depth = 1; //图像深度
image_create_info.mipLevels = 1; //图像 mipmap 级数
image_create_info.arrayLayers = 1; //图像数组层数量
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; //采样模式
image_create_info.tiling = VK_IMAGE_TILING_LINEAR; //采用线性瓦片组织方式
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; //初始布局
image_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; //图像用途
image_create_info.queueFamilyIndexCount = 0; //队列家族数量
image_create_info.pQueueFamilyIndices = NULL; //队列家族索引列表
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; //共享模式
image_create_info.flags = 0; //标志
VkImage textureImage; //纹理对应的图像
VkResult result = vkCreateImage(device, &image_create_info, NULL, &textureImage);
assert(result == VK_SUCCESS); //检查图像创建是否成功
textureImageList[texName] = textureImage; //添加到纹理图像列表
VkMemoryAllocateInfo mem_alloc = {}; //构建内存分配信息结构体实例
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; //结构体的类型
mem_alloc.pNext = NULL; //自定义数据的指针
mem_alloc.allocationSize = 0; //内存字节数
mem_alloc.memoryTypeIndex = 0; //内存类型索引
VkMemoryRequirements mem_reqs; //纹理图像的内存需求
vkGetImageMemoryRequirements(device, textureImage, &mem_reqs);//获取纹理图像的内存需求
mem_alloc.allocationSize = mem_reqs.size; //实际分配的内存字节数
bool flag = memoryTypeFromProperties(memoryroperties, mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
&mem_alloc.memoryTypeIndex); //获取内存类型索引
VkDeviceMemory textureMemory; //创建设备内存实例
result = vkAllocateMemory(device, &mem_alloc, NULL, &(textureMemory)); //分配设备内存
textureMemoryList[texName] = textureMemory; //添加到纹理内存列表
result = vkBindImageMemory(device, textureImage, textureMemory, 0); //绑定图像和内存
uint8_t *pData; // CPU 访问时的辅助指针
vkMapMemory(device, textureMemory, 0,
mem_reqs.size, 0, (void**)(&pData)); //映射内存为 CPU 可访问
memcpy(pData, ctdo->data, mem_reqs.size); //将纹理数据拷贝进设备内存
vkUnmapMemory(device, textureMemory); //解除内存映射
}
//首先构建了图像视图创建信息结构体实例,然后进一步设置了此结构体实例的多项属性,主要包括结构体类型、图像视图的类型和像素格式、RGBA 色彩通道的调和情况、图像视图使用方面及类型、Mipmap 相关、数组层相关等内容。
VkImageViewCreateInfo view_info = {}; //构建图像视图创建信息结构体实例
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; //结构的类型
view_info.pNext = NULL; //自定义数据的指针
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; //图像视图的类型
view_info.format = format; //图像视图的像素格式
view_info.components.r = VK_COMPONENT_SWIZZLE_R; //设置 R 通道调和
view_info.components.g = VK_COMPONENT_SWIZZLE_G; //设置 G 通道调和
view_info.components.b = VK_COMPONENT_SWIZZLE_B; //设置 B 通道调和
view_info.components.a = VK_COMPONENT_SWIZZLE_A; //设置 A 通道调和
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;//图像视图使用方面
view_info.subresourceRange.baseMipLevel = 0; //基础 Mipmap 级别
view_info.subresourceRange.levelCount = 1; //Mipmap 级别的数量
view_info.subresourceRange.baseArrayLayer = 0; //基础数组层
view_info.subresourceRange.layerCount = 1; //数组层的数量
view_info.image = textureImageList[texName]; //对应的图像
//先创建了纹理对应的图像视图,然后根据纹理名称将创建的图像视图添加到图像视图列表中。
VkImageView viewTexture; //纹理图像对应的图像视图
VkResult result = vkCreateImageView(device, &view_info, NULL, &viewTexture);
viewTextureList[texName] = viewTexture; //添加到图像视图列表
//先构建了图像描述信息实例,为其指定了图像视图、采样器和图像布局,并根据纹理名称将其添加到纹理图像描述信息列表中,最后删除了主机内存中的纹理数据。
VkDescriptorImageInfo texImageInfo; //构建图像描述信息实例
texImageInfo.imageView = viewTexture; //采用的图像视图
texImageInfo.sampler = samplerList[0]; //采用的采样器
texImageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;//图像布局
texImageInfoList[texName] = texImageInfo; //添加到纹理图像描述信息列表
delete ctdo; //删除内存中的纹理数据
}