Vulkan 学习(8)---- vkImageView 创建

OverView

Vulkan Components

Vulkan 的图像视图(VkImageView) 用于描述如何访问 VkImage 对象以及访问图像的哪一部分,

图像视图定义了图像格式和访问方式,允许渲染管线和图像进行交互,无论是作为纹理,颜色附件或者深度/模板附件

在交换链中,我们需要为每个图像(VkImage)创建一个基本的图像视图(VkImageView),以便将它们作为颜色附件

创建方法

通过 vkCreateImageView 函数创建 vkImageView,函数原型如下:

VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView(
    VkDevice                                    device,
    const VkImageViewCreateInfo*                pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkImageView*                                pView);

其中的VkImageViewCreateInfo是包含了主要创建信息的结构体:

typedef struct VkImageViewCreateInfo {
    // 结构体类型,必须是 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO
    VkStructureType            sType;
    // 指向扩展特定数据的指针,对于 核心 vulkan 功能,通常为 null
    const void*                pNext;
    // 创建图像视图使用的标志位
    VkImageViewCreateFlags     flags;
    // 对应的 VkImage 对象
    VkImage                    image;
    // 图像视图的类型,决定了视图是如何解释图像的
    VkImageViewType            viewType;
    // 图像视图的格式,决定了图像数据的解释方式
    VkFormat                   format;
    // 用于在创建视图时重新映射图像的颜色分量
    VkComponentMapping         components;
    // 定义了图像视图的子资源范围,包括 mipmap 级别,数组层以及深度层的范围
    VkImageSubresourceRange    subresourceRange;
} VkImageViewCreateInfo;

其中关键的 VkImageViewTypeVkComponentMappingVkImageSubresourceRange 需要重点关注:

关键结构

VkImageViewType 定义了图像视图的类型:

typedef enum VkImageViewType {
    VK_IMAGE_VIEW_TYPE_1D = 0,
    VK_IMAGE_VIEW_TYPE_2D = 1,
    VK_IMAGE_VIEW_TYPE_3D = 2,
    VK_IMAGE_VIEW_TYPE_CUBE = 3,
    VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4,
    VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5,
    VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6,
    VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkImageViewType;

VkComponentMapping 用于指定如何从源图像中的颜色分量映射到目标图像视图中的颜色分量
通过 VkComponentMapping 类型的属性(components),可以灵活控制图像视图中每个颜色分量的来源,这对于图像处理非常有用,比如转换颜色格式,调整颜色通道等。

默认情况下,我们将每个分量设置为VK_COMPONENT_SWIZZLE_IDENTITY,这意味着它们将直接从源图像中取值:

typedef enum VkComponentSwizzle {
    VK_COMPONENT_SWIZZLE_IDENTITY = 0, // 使用原始值
    VK_COMPONENT_SWIZZLE_ZERO = 1, // 使用零值
    VK_COMPONENT_SWIZZLE_ONE = 2, // 使用一值
    VK_COMPONENT_SWIZZLE_R = 3, // 使用 R 分量
    VK_COMPONENT_SWIZZLE_G = 4, // 使用 G 分量
    VK_COMPONENT_SWIZZLE_B = 5, // 使用 B 分量
    VK_COMPONENT_SWIZZLE_A = 6, // 使用 A 分量
    VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF
} VkComponentSwizzle;

typedef struct VkComponentMapping {
    VkComponentSwizzle    r; // r 分量 来源
    VkComponentSwizzle    g; // g 分量 来源
    VkComponentSwizzle    b; // b 分量 来源
    VkComponentSwizzle    a; // a 分量 来源
} VkComponentMapping;

VkImageSubresourceRange 允许你选择图像的哪些层面和 mip 级别应该被应用在图像视图中:

typedef struct VkImageSubresourceRange {
    // 需要包含的图像 aspect(图像特性的层面)
    VkImageAspectFlags    aspectMask;
    // mip 级别的起始级别
    uint32_t              baseMipLevel;
    // mip 级别的数量
    uint32_t              levelCount;
    // 数组层的起始索引
    uint32_t              baseArrayLayer;
    // 数组层数量
    uint32_t              layerCount;
} VkImageSubresourceRange;

aspectMask 用于指定需要包含在图像视图中图像aspect(图像的特性的层面),
比如图像有深度和/或模板信息,你可以选择只包括颜色方面
(VK_IMAGE_ASPECT_COLOR_BIT),或者包括深度方面 (VK_IMAGE_ASPECT_DEPTH_BIT) 和/或模板方面 (VK_IMAGE_ASPECT_STENCIL_BIT)

参考代码

这里一般和VkImage一起创建 demo,因为VkImageView 的创建需要包含 VkImage 对象 作为参数:

void vulkanBasicDemo::vulkanCreateVulkanImage() {
    
    VkImageCreateInfo imageInfo = {};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;  // 结构体类型
    imageInfo.pNext = nullptr;                              // 指向扩展信息的指针
    imageInfo.flags = 0;                                    // 图像创建标志
    imageInfo.imageType = VK_IMAGE_TYPE_2D;                 // 图像类型为 2D
    imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;            // 图像格式
    imageInfo.extent.width = test_width;                      // 图像宽度
    imageInfo.extent.height = test_height;                    // 图像高度
    imageInfo.extent.depth = 1;                             // 图像深度(对于 2D 图像为 1)
    imageInfo.mipLevels = 1;                                // MIP 级别数
    imageInfo.arrayLayers = 1;                              // 图像数组层数
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;              // 多重采样数量(1 表示不使用多重采样)
    imageInfo.tiling = VK_IMAGE_TILING_LINEAR;             // 图像数据的存储方式(优化存储)
    imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // 图像用途(作为传输目标和采样器)
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;      // 共享模式(独占模式)
    imageInfo.queueFamilyIndexCount = 0;                    // 使用队列族索引的数量
    imageInfo.pQueueFamilyIndices = &graphicsQueueFamilyIndex;      // 队列族索引的指针
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;    // 图像的初始布局(未定义)

    VkImage textureImage; // 定义 VkImage 变量
    // 创建图像
    if (vkCreateImage(device, &imageInfo, nullptr, &textureImage) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture image!"); // 如果创建图像失败,抛出异常
    }
    std::cout << __FUNCTION__ <<" vkCreateImage create image successs!" << std::endl;


    // 获取图像的内存需求
    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(device, textureImage, &memRequirements);

    // 创建 VkMemoryAllocateInfo 结构体并初始化
    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);                 // 内存类型索引

    VkDeviceMemory textureImageMemory; // 定义 VkDeviceMemory 变量
    // 分配内存
    if (vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate texture image memory!"); // 如果分配内存失败,抛出异常
    }

    // 将内存绑定到图像
    vkBindImageMemory(device, textureImage, textureImageMemory, 0);
    std::cout << "vkBindImageMemory Successs!" << std::endl;


    // 定义 VkImageViewCreateInfo 结构体并初始化
    VkImageViewCreateInfo viewInfo = {};
    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;  // 结构体类型
    viewInfo.pNext = nullptr;                                   // 指向扩展信息的指针
    viewInfo.flags = 0;                                         // 图像视图创建标志
    viewInfo.image = textureImage;                              // 要创建视图的图像句柄
    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;                  // 图像视图类型为 2D
    viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;                  // 图像视图的格式
    viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;      // R 分量映射
    viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;      // G 分量映射
    viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;      // B 分量映射
    viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;      // A 分量映射
    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // 视图的子资源范围(颜色部分)
    viewInfo.subresourceRange.baseMipLevel = 0;                 // 基础 MIP 级别
    viewInfo.subresourceRange.levelCount = 1;                   // MIP 级别数量
    viewInfo.subresourceRange.baseArrayLayer = 0;               // 基础数组层
    viewInfo.subresourceRange.layerCount = 1;                   // 数组层数量
    
    VkImageView textureImageView; // 定义 VkImageView 变量
    // 创建图像视图
    if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
         throw std::runtime_error("failed to create texture image view!"); // 如果创建视图失败,抛出异常
    }
    std::cout << __FUNCTION__ << " vkCreateImageView create imageView successs!" << std::endl;

    // 使用完成后销毁资源
    vkDestroyImageView(device, textureImageView, nullptr);  // 销毁图像试图
    vkDestroyImage(device, textureImage, nullptr);         // 销毁图像
    vkFreeMemory(device, textureImageMemory, nullptr);     // 释放图像内存
    std::cout << "vkDestroyImage Successs!" << std::endl;
}
<think>嗯,用户问的是Vulkan如何创建图像。首先,我需要回忆一下Vulkan的图像创建流程。Vulkan作为底层图形API,图像创建涉及多个步骤,需要用到不同的结构体和函数。可能用户是刚开始学习Vulkan,或者想深入理解其内部机制,所以需要详细但清晰的步骤说明。 首先,Vulkan中图像由VkImage对象表示。创建图像需要调用vkCreateImage函数。那这个函数需要什么参数呢?对了,需要设备、创建信息结构体、分配器指针和指向VkImage的指针。不过,在填充VkImageCreateInfo结构体之前,必须确定图像的类型、格式、尺寸、用途等属性。用户可能不太清楚这些参数的设置,需要逐一解释。 然后,图像的内存分配。Vulkan要求手动分配内存,所以需要先查询图像的内存需求,使用vkGetImageMemoryRequirements。接着分配内存,vkAllocateMemory,然后将内存绑定到图像对象,vkBindImageMemory。这部分可能容易出错,用户需要特别注意内存类型的选择,是否符合需求。 另外,图像布局的转换也很重要。新创建的图像初始布局是UNDEFINED,需要通过管道屏障或渲染流程转换为适合后续操作的布局,比如GENERAL或SHADER_READ_ONLY_OPTIMAL。用户可能不了解布局转换的必要性,这里需要解释清楚。 可能用户在实际操作中会遇到问题,比如内存类型不合适导致分配失败,或者图像格式不支持,或者忘记绑定内存。这时候应该提醒常见的错误点,以及如何排查,比如检查内存类型、使用验证层等。 另外,用户可能想知道为什么需要这么多步骤,而其他API可能更简单。这时候可以说明Vulkan的设计哲学,给予开发者更多的控制权,但需要更多的设置。这可能帮助用户理解为何流程如此复杂。 最后,是否需要提到图像视图(VkImageView)的创建?虽然用户的问题是关于图像本身的创建,但图像视图在渲染中是必须的,可能用户接下来会用到,所以可以简要提及,作为创建后的下一步操作。 总结步骤应该是:创建VkImage对象,查询内存需求,分配内存,绑定内存,布局转换,以及可能的图像视图创建。需要分步骤详细说明每个部分,并强调关键参数和注意事项。同时,给出代码示例会更直观,但用户可能只需要概念性的解释,所以可以酌情提供伪代码或结构体示例。 另外,要确保使用中文,避免专业术语的混淆,必要时对术语进行解释,比如“图像布局”、“内存类型”等。结构清晰,逐步引导用户理解整个流程,确保回答真实可靠,覆盖可能的问题点。</think>Vulkan创建图像的核心流程可分为以下步骤,请逐步理解: 1. 创建VkImage对象 - 使用`VkImageCreateInfo`结构体配置图像属性 ```cpp VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; // 图像维度类型 imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB; // 像素格式 imageInfo.extent = {width, height, 1}; // 尺寸(深度设为1) imageInfo.mipLevels = 1; // mipmap层级 imageInfo.arrayLayers = 1; // 数组层数 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; // 采样数 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; // 内存布局方式 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; // 使用场景 ``` - 调用`vkCreateImage`生成图像对象: ```cpp VkImage textureImage; vkCreateImage(device, &imageInfo, nullptr, &textureImage); ``` 2. 分配设备内存 - 查询图像内存需求: ```cpp VkMemoryRequirements memRequirements; vkGetImageMemoryRequirements(device, textureImage, &memRequirements); ``` - 选择合适的内存类型(需满足`memRequirements.memoryTypeBits`): ```cpp 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); ``` - 分配内存并绑定: ```cpp VkDeviceMemory textureImageMemory; vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory); vkBindImageMemory(device, textureImage, textureImageMemory, 0); ``` 3. 转换图像布局(重要步骤) - 初始布局为`VK_IMAGE_LAYOUT_UNDEFINED`,需转换为可用布局: ```cpp VkCommandBuffer cmdBuffer = beginSingleTimeCommands(); VkImageMemoryBarrier barrier{}; barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // ... 其他屏障参数设置 vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); endSingleTimeCommands(cmdBuffer); ``` 关键注意事项: 1. 内存类型选择必须匹配图像的使用场景(如`DEVICE_LOCAL`用于高频访问) 2. 图像布局转换必须通过管道屏障完成 3. 需创建对应的`VkImageView`才能用于着色器采样 4. 多级mipmap需要单独生成 5. 建议使用`VMA`库简化内存管理 典型错误排查点: - 检查`vkCreateImage`返回结果 - 验证内存类型是否支持需求 - 确认图像格式是否被物理设备支持 - 确保内存绑定偏移量符合对齐要求 完整图像创建流程需配合命令缓冲区提交,建议通过工具函数封装常用操作。后续还需创建图像视图和采样器才能完成纹理的完整使用链路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值