当前位置: 首页 > news >正文

个人网站 备案 备注旅游网站排名排行榜

个人网站 备案 备注,旅游网站排名排行榜,帮人做传销网站违法吗,外贸网站建设 义乌目录 23 图像 图片库 暂存缓冲区 纹理图像 布局转换 将缓冲区复制到图像上 准备纹理图像 传输屏障掩码 清除 24 图像视图和采样器 纹理图像视图 采样器 Anisotropy 设备特征 25 组合图像采样器 更新描述符 纹理坐标系 着色器 23 图像 添加纹理将涉及以下步骤&am…

目录

23 图像

图片库

 暂存缓冲区

        纹理图像

布局转换

将缓冲区复制到图像上

准备纹理图像

传输屏障掩码

清除

24  图像视图和采样器

纹理图像视图

采样器

Anisotropy 设备特征

 25 组合图像采样器

更新描述符

纹理坐标系

着色器


23 图像

添加纹理将涉及以下步骤:

  • 创建一个由设备内存支持的图像对象
  • 用图像文件中的像素填充它
  • 创建图像采样器
  • 添加组合图像采样器描述符以从纹理中采样颜色

我们之前已经使用过图像对象,但它们是由交换链扩展自动创建的。这次我们必须自己创建一个。创建图像并用数据填充它类似于创建顶点缓冲区。我们将从创建暂存资源并用像素数据填充它开始,然后将其复制到我们将用于渲染的最终图像对象。我们将首先创建这个缓冲区并用像素值填充它,然后我们将创建一个图像来将像素复制到。

图像可以有不同的布局,这些布局会影响像素在内存中的组织方式。例如,由于图形硬件的工作方式,简单地逐行存储像素可能不会带来最佳性能。在对图像执行任何操作时,您必须确保它们具有最适合该操作的布局。

  • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: 最适合展示
  • VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: 最适合作为从片段着色器写入颜色的附件
  • VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:最适合作为传输操作中的源,例如vkCmdCopyImageToBuffer
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:最适合作为传输操作中的目的地,例如vkCmdCopyBufferToImage
  • VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:最适合从着色器采样

图片库

有许多库可用于加载图像,您甚至可以编写自己的代码来加载 BMP 和 PPM 等简单格式。 stb_image 库。stb_image库实现都写在头文件中,不需要编译成库,项目中直接引用头文件目录即可。

GitHub - nothings/stb: stb single-file public domain libraries for C/C++

项目属性 ----> C/C++ —> 附加包含目录 —> your_path\stb-master

新建文件夹textures,放入图像texture.jpg,将其大小调整为 512 x 512 像素,但您可以随意选择您想要的任何图像。该库支持最常见的图像文件格式,如 JPEG、PNG、BMP 和 GIF。

包含图像库:

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
void createTextureImage(){//创建一个新函数createTextureImage,我们将在其中加载图像并将其上传到 Vulkan 图像对象中int texWidth, texHeight, texChannels;stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);VkDeviceSize imageSize = texWidth * texHeight * 4;
//stbi_load函数将要加载的文件路径和通道数作为参数。该STBI_rgb_alpha值强制使用 alpha 通道加载图像if (!pixels) {throw std::runtime_error("failed to load texture image!");}}

 暂存缓冲区

VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;//缓冲区应该在主机可见内存中,以便我们可以映射它
createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
//从图像加载库中获取的像素值复制到缓冲区中:void* data;
vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(device, stagingBufferMemory);
//清理原始像素阵列:stbi_image_free(pixels);

纹理图像

虽然我们可以设置着色器来访问缓冲区中的像素值,但最好使用 Vulkan 中的图像对象来实现此目的。通过允许我们使用 2D 坐标,图像对象将使检索颜色变得更容易和更快。图像对象中的像素称为纹素,添加以下新类成员:

VkImage textureImage;
VkDeviceMemory textureImageMemory;
//图像的参数在VkImageCreateInfo结构中指定:VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
//imageType字段中指定的图像类型告诉 Vulkan 使用哪种坐标系来处理图像中的纹素
//创建 1D、2D 和 3D 图像
imageInfo.imageType = VK_IMAGE_TYPE_2D;
//该extent字段指定图像的尺寸,基本上是每个轴上有多少纹素
imageInfo.extent.width = static_cast<uint32_t>(texWidth);
imageInfo.extent.height = static_cast<uint32_t>(texHeight);
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
//该usage字段与缓冲区创建期间的语义相同。该图像将用作缓冲区副本的目标,因此应将其设置为传输目标。
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;//支持图形(因此也支持)传输操作的队列系列
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;//samples标志与多重采样有关
imageInfo.flags = 0; // Optionalif (vkCreateImage(device, &imageInfo, nullptr, &textureImage) != VK_SUCCESS) {throw std::runtime_error("failed to create image!");
}

tiling字段可以具有以下两个值之一:

  • VK_IMAGE_TILING_LINEARpixels: Texels 像我们的数组一样按行优先顺序排列
  • VK_IMAGE_TILING_OPTIMAL:纹理元素按照实现定义的顺序排列,以实现最佳访问
  • 如果您希望能够直接访问图像内存中的纹素,那么您必须使用VK_IMAGE_TILING_LINEAR

initialLayout图像的的 只有两个可能的值:

  • VK_IMAGE_LAYOUT_UNDEFINED: GPU 不可用,第一个转换将丢弃纹素。
  • VK_IMAGE_LAYOUT_PREINITIALIZED: GPU 不可用,但第一个转换将保留纹素。

图像是用vkCreateImage创建的,它没有任何特别值得注意的参数。VK_FORMAT_R8G8B8A8_SRGB格式有可能不被图形硬件所支持。

 为图像分配内存的方法与为缓冲区分配内存的方法完全相同。使用vkGetImageMemoryRequirements代替vkGetBufferMemoryRequirements,使用vkBindImageMemory代替vkBindBufferMemory 。

VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device, textureImage, &memRequirements);VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);if (vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) {throw std::runtime_error("failed to allocate image memory!");
}vkBindImageMemory(device, textureImage, textureImageMemory, 0);

这个函数已经变得相当大了,而且在后面的章节中还需要创建更多的图像,所以我们应该把图像创建抽象成一个createImage函数,就像我们对缓冲区所做的那样。创建这个函数,并将图像对象的创建和内存分配移到它上面。

布局转换

我们现在要写的函数涉及到再次记录和执行一个命令缓冲区,所以现在是把这个逻辑移到一两个辅助函数中的好时机:

VkCommandBuffer beginSingleTimeCommands() {VkCommandBufferAllocateInfo allocInfo{};allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;allocInfo.commandPool = commandPool;allocInfo.commandBufferCount = 1;VkCommandBuffer commandBuffer;vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);VkCommandBufferBeginInfo beginInfo{};beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;vkBeginCommandBuffer(commandBuffer, &beginInfo);return commandBuffer;
}void endSingleTimeCommands(VkCommandBuffer commandBuffer) {vkEndCommandBuffer(commandBuffer);VkSubmitInfo submitInfo{};submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;submitInfo.commandBufferCount = 1;submitInfo.pCommandBuffers = &commandBuffer;vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);vkQueueWaitIdle(graphicsQueue);vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
}

copyBuffer的现有代码。现在你可以将该函数简化为:

void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {VkCommandBuffer commandBuffer = beginSingleTimeCommands();VkBufferCopy copyRegion{};copyRegion.size = size;vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);endSingleTimeCommands(commandBuffer);
}

创建一个新的函数来处理布局的转换:

void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {VkCommandBuffer commandBuffer = beginSingleTimeCommands();
//执行布局转换的最常见方法之一是使用图像内存屏障
//屏障通常用于同步访问资源,比如确保在从缓冲区读取之前完成对缓冲区的写
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
//前两个字段指定布局过渡。如果你不关心图片的现有内容,可以使用VK_IMAGE_LAYOUT_UNDEFINED作为oldLayout
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
//如果你使用屏障来转移队列家族的所有权,那么这两个字段应该是队列家族的索引
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
//image和subresourceRange指定受影响的图像和图像的具体部分。
//我们的图像不是一个数组,也没有MIP映射层,所以只指定了一个level 和layer 
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer,0 /* TODO */, 0 /* TODO */,0,0, nullptr,0, nullptr,1, &barrier
);//指定哪些涉及资源的操作类型必须在障碍物之前发生,哪些涉及资源的操作必须在障碍物上等待endSingleTimeCommands(commandBuffer);
}

将缓冲区复制到图像上

 就像缓冲区拷贝一样,你需要指定缓冲区的哪一部分将被拷贝到图像的哪一部分。这是通过VkBufferImageCopy结构实现的

void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {VkCommandBuffer commandBuffer = beginSingleTimeCommands();VkBufferImageCopy region{};
region.bufferOffset = 0;
//bufferOffset “指定了像素值开始在缓冲区中的字节偏移。
//bufferRowLength”和 “bufferImageHeight”字段指定像素在内存中的排列方式
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
//这两个字段中指定 0表示像素像我们的情况一样被紧密地排列region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;region.imageOffset = {0, 0, 0};
region.imageExtent = {width,height,1
};//缓冲区到图像的复制操作是通过vkCmdCopyBufferToImage函数排队的
vkCmdCopyBufferToImage(commandBuffer,buffer,image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,1,&region
);endSingleTimeCommands(commandBuffer);
}

准备纹理图像

我们现在拥有完成设置纹理图像所需的所有工具,所以我们要回到createTextureImage函数。我们在那里做的最后一件事是创建纹理图像。下一步是将暂存缓冲区复制到纹理图像上。这包括两个步骤。

  • 将纹理图像过渡到VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  • 执行缓冲区到图像的复制操作

传输屏障掩码

如果你现在启用了验证层来运行你的应用程序,那么你会看到它抱怨transitionImageLayout中的访问掩码和管道阶段是无效的。我们仍然需要根据过渡中的布局来设置这些。

有两个过渡我们需要处理。

  • Undefined → transfer destination:传输写入,不需要等待任何东西
  • Transfer destination → shader reading:着色器读取应该等待传输写入,特别是片段着色器中的着色器读取,因为那是我们要使用纹理的地方。

这些规则是用以下访问掩码和管线阶段指定的:

VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {barrier.srcAccessMask = 0;barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
//传输写入必须发生在流水线传输阶段。由于写操作不需要等待任何东西,
//你可以指定一个空的访问掩码和最早的流水线阶段VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT来进行前障操作sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
//VK_PIPELINE_STAGE_TRANSFER_BIT不是图形和计算管道中*真实的阶段。它更像是一个发生传输的伪阶段sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {throw std::invalid_argument("unsupported layout transition!");
}vkCmdPipelineBarrier(commandBuffer,sourceStage, destinationStage,0,0, nullptr,0, nullptr,1, &barrier
);

清除

完成createTextureImage函数,在最后清理暂存器和它的内存:

    transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);vkDestroyBuffer(device, stagingBuffer, nullptr);vkFreeMemory(device, stagingBufferMemory, nullptr);//主纹理图像会一直使用到程序结束:
void cleanup() {cleanupSwapChain();vkDestroyImage(device, textureImage, nullptr);vkFreeMemory(device, textureImageMemory, nullptr);...
}

24  图像视图和采样器

纹理图像视图

我们之前已经看到,在交换链图像和帧缓冲区中,图像是通过图像视图而不是直接访问。我们也需要为纹理图像创建这样一个图像视图。

添加一个类成员,为纹理图像保存一个VkImageView,并创建一个新的函数createTextureImageView,我们将在这里创建它:


VkImageView textureImageView;
//因为很多逻辑与createImageViews重复,你可能希望将其抽象为一个新的createImageView函数:VkImageView createImageView(VkImage image, VkFormat format) {VkImageViewCreateInfo viewInfo{};viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;viewInfo.image = image;viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;viewInfo.format = format;viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;viewInfo.subresourceRange.baseMipLevel = 0;viewInfo.subresourceRange.levelCount = 1;viewInfo.subresourceRange.baseArrayLayer = 0;viewInfo.subresourceRange.layerCount = 1;VkImageView imageView;if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {throw std::runtime_error("failed to create texture image view!");}return imageView;
}

采样器

着色器有可能直接从图像中读取纹理,但当它们被用作纹理时,这并不是很常见。纹理通常是通过采样器访问的,它将应用滤波和变换来计算被检索的最终颜色。

这些过滤器有助于处理诸如过采样的问题。考虑一个被映射到几何体上的纹理,它的碎片多于纹理。如果你只是在每个片段的纹理坐标中选择最接近的texel,那么你会得到像第一张图片那样的结果。如果你通过线性插值将4个最接近的texel结合起来,那么你会得到一个更平滑的结果 

欠采样是一个相反的问题,即你的纹理比片段多。这将导致在对高频图案进行采样时出现伪影,比如在一个尖锐的角度对棋盘纹理进行采样。

除了这些滤镜之外,采样器还可以处理变换问题。它决定了当你试图通过它的addressing模式读取图像外的文本时会发生什么。下面的图片显示了一些可能性。

 创建一个函数createTextureSampler来设置这样一个采样器对象。稍后我们将使用这个采样器从着色器的纹理中读取颜色。

void createTextureSampler() {
//采样器是通过VkSamplerCreateInfo结构配置的,该结构指定了它应该应用的所有过滤器和转换。
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
//magFilter和minFilter字段指定了如何插值被放大或缩小的文本。
//放大涉及到上面描述的过度取样问题,缩小涉及到欠取样问题
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;//寻址模式可以通过 addressMode 字段指定每个轴
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;//这两个字段指定是否应该使用各向异性过滤
samplerInfo.anisotropyEnable = VK_TRUE;
//maxAnisotropy “字段限制了可用于计算最终颜色的texel样本量。
//samplerInfo.maxAnisotropy = ???;
//一个较低的值会带来更好的性能,但质量较低。
//为了弄清我们可以使用哪个值,我们需要检索物理设备的属性:VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
//borderColor 字段指定了当用钳制边界寻址模式在图像之外取样时返回哪种颜色。
//可以返回黑色、白色或透明的浮点数或英寸格式
//unnormalizedCoordinates字段指定了你想用哪种坐标系统来处理图像中的纹理。
//如果这个字段是VK_TRUE,那么你可以简单地使用[0, texWidth)和[0, texHeight)范围内的坐标。
//如果它是VK_FALSE,那么在所有轴上都使用[0, 1)范围来处理texels。
//
samplerInfo.unnormalizedCoordinates = VK_FALSE;samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
//如果启用了比较功能,那么texels将首先与一个值进行比较,而比较的结果将用于过滤操作samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
}

 注意,轴被称为U、V和W,而不是X、Y和Z,这是纹理空间坐标的惯例。

  • vk_sampler_address_mode_repeat。当超出图像尺寸时重复纹理。
  • vk_sampler_address_mode_mirrored_repeat。和重复一样,但当超出尺寸时,将坐标倒置以镜像图像。
  • vk_sampler_address_mode_clamp_to_edge: 取最接近坐标的边缘的颜色,超出图像尺寸。
  • vk_sampler_address_mode_mirror_clamp_to_edge: 和钳制边缘一样,但使用与最接近的边缘相反的边缘。
  • vk_sampler_address_mode_clamp_to_border: 当取样超出图像的尺寸时,返回一个纯色。

采样器的功能现在已经完全定义了。添加一个类成员来保存采样器对象的句柄,用vkCreateSampler创建采样器:

VkSampler textureSampler;if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {throw std::runtime_error("failed to create texture sampler!");}

Anisotropy 设备特征

anisotropic filtering实际上是一个可选的设备功能

VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = VK_TRUE;//而即使现代显卡不支持它的可能性很小,我们也应该更新isDeviceSuitable来检查它是否可用:bool isDeviceSuitable(VkPhysicalDevice device) {...VkPhysicalDeviceFeatures supportedFeatures;
//vkGetPhysicalDeviceFeatures重新利用了VkPhysicalDeviceFeatures结构,通过设置布尔值来指示支持哪些功能,而不是要求哪些功能。vkGetPhysicalDeviceFeatures(device, &supportedFeatures);return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
}

 25 组合图像采样器

组合图像采样器使得着色器可以通过采样器对象来访问图像资源。我们将首先修改描述符布局、描述符池和描述符集,以包括这样一个组合图像取样器描述符。之后,我们将为Vertex添加纹理坐标,并修改片段着色器以从纹理中读取颜色,而不是仅仅插值顶点的颜色。

更新描述符

浏览createDescriptorSetLayout函数,为组合图像采样器添加一个VkDescriptorSetLayoutBinding。我们将简单地把它放在统一缓冲区之后的绑定中:

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;
//确保设置`stageFlags’以表明我们打算在片段着色器中使用组合图像采样器描述符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();

我们还必须创建一个更大的描述符池,为组合图像采样器的分配腾出空间,在VkDescriptorPoolCreateInfo中添加另一个类型为VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER的VkPoolSize 。转到createDescriptorPool函数,并修改它,为这个描述符包括一个VkDescriptorPoolSize:

std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);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>(MAX_FRAMES_IN_FLIGHT);//最后一步是将实际的图像和采样器资源与描述符集中的描述符绑定。转到createDescriptorSets函数。for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; 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;...
}

组合图像采样器结构的资源必须在VkDescriptorImageInfo结构中指定,就像统一缓冲区描述符的缓冲区资源在VkDescriptorBufferInfo结构中指定。 

纹理坐标系

纹理映射还缺少一个重要成分,那就是每个顶点的实际坐标。坐标决定了图像是如何实际映射到几何体上的。

glm::vec2 texCoord;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}}
};

将通过使用左上角的 0, 0到右下角的 1, 1的坐标来简单地用纹理填充正方形。请随意尝试不同的坐标。试着使用低于0或高于1的坐标,看看寻址模式是如何运作的!

着色器

最后一步是修改着色器以从纹理中提取颜色。我们首先需要修改顶点着色器,将纹理坐标传递给片段着色器:不要忘记重新编译着色器!

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;
}#version 450layout(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);
}

绿色通道代表水平坐标,红色通道代表垂直坐标。黑角和黄角确认纹理坐标在整个广场上从 0,01,1正确插值。

一个组合的图像采样器描述符在GLSL中由一个采样器统一表示。在片段着色器中添加一个对它的引用:

layout(binding = 1) uniform sampler2D texSampler;void main() {outColor = texture(texSampler, fragTexCoord);outColor = texture(texSampler, fragTexCoord * 2.0);outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0);
}

纹理是使用内置的texture函数进行采样的。它需要一个 “采样器”和坐标作为参数。采样器会在后台自动处理过滤和转换的问题。现在当你运行应用程序时,你应该看到广场上的纹理了。

 

 

http://www.yayakq.cn/news/548148/

相关文章:

  • 软件定制网站建设国内免费域名申请
  • 婚恋网站开发平台代理招商wordpress扫码提交数据
  • 各网站提交入口大连哪里做网站
  • 卖主机 服务器的网站赣州吾往矣网络科技有限公司
  • 上海网站排名优化费用自己制作网站app
  • 打造对外宣传工作平台网站建设天津猎头公司
  • dede网站数据库路径深圳光明区住房和建设局官网
  • 河南省建设厅注册中心网站百度查找相似图片
  • 二级域名可以做不同的网站吗阿里巴巴的网站二维码怎么做
  • 1个服务器可以做多少个网站免费的黄冈网站有哪些
  • 辽宁工程监督西安网站seo外包
  • 旅游网站开发盈利模式中科诚建建设工程有限公司网站
  • 精通网站建设工资多少做网站有哪些类型
  • 公司营销网站建设百度网做网站吗
  • 自己做视频网站能赚钱吗软件属于网站开发吗
  • 诸城网站建设多少钱有关网站建设有那些功能
  • 网站倒计时代码yes风淘宝网站
  • 个人网站备案番禺网站制作价格
  • 网站开发 系统需求文档深圳外贸业务员工资
  • 哪种网站开发最简单深圳哪家做网站
  • 企业网站模板大全wordpress 4.7.3 漏洞
  • 旅游网站品牌建设wordpress文章支持多形式
  • 代做广联达 的网站商丘电子商务网站建设
  • 商务网站开发的基本流程华为商城网站设计分析
  • 北大青鸟网站建设课程全网网站推广
  • 网站要挂工商标识怎么做网站开发企业组织结构
  • 网站开发pmp网站模板哪家好
  • 之梦英语版网站怎么做云建站精品模版
  • 河北公司网站建设效果苏州有什么好玩的地方
  • 宁波优化推广外贸seo搜索优化