文章目录
- Texture
- Texture::LoadImage()
本篇不叨叨了,接着 上篇。 本篇介绍如何创建纹理图片。
Texture
这个类的作用是传入一个图片的路径,构建纹理图片和Sampler。
Texture::LoadImage()
这个方法是创建纹理Image
代码比较长,我先把代码的逻辑介绍一下。
- 使用stb库读取一个图片。
- 创建一个CPU可写的Buffer,并将图片的像素信息拷贝到Buffer中。这个操作与【Vulkan入门】16-IndexBuffer中的操作是一样的。
- 创建一个位于GPU(Device)内存上的Image
- 转换Image的Layout
- 将Buffer中的内容拷贝到Image中。
- 转换Image的Layout
- 创将ImageView。 在【Vulkan入门】09-CreateFrameBuffer中提到过,Vulkan不会直接操作Image,而是通过ImageView来操作Image。
STB库 https://github.com/nothings/stb.git
这是一个操作图片的库。这个库的特点是所有实现都在一个头文件里完成。因此只要将所需要的头文件拷贝的项目中就可以直接使用了。
这里使用了stb_image.h
中来读取图像,在引用这个头文件的上面添加#define STB_IMAGE_IMPLEMENTATION
,便可以直接使用了。
为什么要转变Image的Layout,之前介绍过Image和Buffer的最大区别是,Image是具有特殊用途的内存空间。因此Vulkan会根据不同的用途对图片在内存中的布局(Layout)进行调整,已优化对Image的使用效率。比如在拷贝前将Image的Layout设置为
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
,Vulkan将Image视为拷贝的目标进行优化。在拷贝后将Image的Layout设置为VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
,Vulkan会将Image视为采样器的纹理进行优化。
使用
vkCmdPipelineBarrier
接口可以转换Image的Layout。 这个接口名可能会导致一些误解,但Vulkan就是这么定义的只能接受。这个Command主要作用其实是保证对内存访问的一致性,其副作用是转换Image的Layout。
void Texture::LoadImage()
{stbi_uc* pixels = stbi_load(m_imagePath.c_str(),&m_texWidth,&m_texHeight, &m_texChannels,STBI_rgb_alpha);m_texSize = m_texWidth * m_texHeight * 4;if (!pixels) {throw std::runtime_error("failed to load texture image!");}VkBuffer stagingBuffer;VkDeviceMemory stagingBufferMemory;MemoryTool::CreateBuffer(m_physicalDevice,m_device,m_texSize,VK_BUFFER_USAGE_TRANSFER_SRC_BIT,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,stagingBuffer,stagingBufferMemory);void* data;vkMapMemory(m_device, stagingBufferMemory, 0, m_texSize, 0, &data);memcpy(data, pixels, static_cast<size_t>(m_texSize));vkUnmapMemory(m_device, stagingBufferMemory);stbi_image_free(pixels);MemoryTool::CreateImage(m_physicalDevice,m_device,m_texWidth,m_texHeight,VK_FORMAT_R8G8B8A8_SRGB,VK_IMAGE_TILING_OPTIMAL,VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_textureImage,m_textureImageMemory);TransitionImageLayout(m_textureImage,VK_FORMAT_R8G8B8A8_SRGB,VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);CopyBufferToImage(stagingBuffer,m_textureImage,static_cast<uint32_t>(m_texWidth),static_cast<uint32_t>(m_texHeight));TransitionImageLayout(m_textureImage,VK_FORMAT_R8G8B8A8_SRGB,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);CreateTextureSampler();vkDestroyBuffer(m_device, stagingBuffer, nullptr);vkFreeMemory(m_device, stagingBufferMemory, nullptr);
}