线性过滤支持

虽然使用内建的vkCmdBlitImage指令来生成纹理的所有细化级别非常方便,但并非所有平台都支持这一指令。它需要我们使用的纹理图像格式支持线性过滤特性。我们可以通过调用vkGetPhysicalDeviceFormatProperties函数来检查这一特性是否被支持。

首先,为generateMipmaps函数添加一个参数用于指定使用的纹理图像格式:

void createTextureImage() {
        ...

    generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_UNORM, texWidth, texHeight, mipLevels);
}

void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {

        ...
}

在generateMipmaps函数中,调用vkGetPhysicalDeviceFormatProperties函数查询指定的纹理图像格式的属性:

void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {

    // Check if image format supports linear blitting
    VkFormatProperties formatProperties;
    vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);

        ...

VkFormatProperties结构体包含了linearTilingFeatures、optimalTilingFeatures和bufferFeatures三个成员变量,每个成员变量描述了在使用对应模式下,可以使用的特性。我们的纹理图像使用优化的tiling模式创建,所以我们需要检查optimalTilingFeatures成员变量来确定我们使用的纹理图像格式是否支持线性过滤特性:

if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
    throw std::runtime_error("texture image format does not support linear blitting!");
}

此外,我们还可以编写一个函数遍历常见的纹理图像格式,查找其中支持线性块传输的格式。或者使用可以生成纹理细化级别的库,比如stb_image_resize来生成纹理的所有细化级别,然后使用加载源图像数据一样的方法加载它们。

通常实践中很少在运行时动态生成纹理的细化级别,而是预先生成然后存储在文件中由程序直接加载所有细化级别的纹理图像。