Vulkan 学习笔记15—Mipmap 与多重采样
一、Mipmap 生成总结
一、Mipmap 基础概念
- 定义:Mipmap 是图像预先计算的缩小版本,每个层级宽高为前一层的一半,用作细节级别(LOD)。
- 作用:
- 远离相机的对象使用较小层级采样,提升渲染速度。
- 避免莫尔条纹等伪影。
- 存储方式:在 Vulkan 中存储于
VkImage
的不同 mip 级别,级别 0 为原始图像,后续层级构成 mip 链。
二、图像创建与 mip 级别计算
-
计算 mip 级别数:
mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
- 原理:通过最大维度的对数计算可被 2 整除的次数,加 1 包含原始图像。
-
修改关键函数:
createImage
:添加mipLevels
参数,设置imageInfo.mipLevels
。createImageView
:设置viewInfo.subresourceRange.levelCount
为mipLevels
。transitionImageLayout
:设置barrier.subresourceRange.levelCount
为mipLevels
。
-
函数调用更新示例:
// 纹理图像创建(含 mipLevels) createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, ...); // 纹理图像视图创建 textureImageView = createImageView(textureImage, ..., mipLevels);
三、Mipmap 生成流程
-
准备工作:
- 为纹理图像添加
VK_IMAGE_USAGE_TRANSFER_SRC_BIT
使用标志。 - 确保图像布局为
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
。
- 为纹理图像添加
-
核心函数
generateMipmaps
:- 循环处理每个 mip 级别(从 1 开始):
- 转换源层级布局:将
i-1
层级转为VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
。 - 设置 blit 操作:
VkImageBlit blit{}; blit.srcSubresource.mipLevel = i - 1; // 源层级 blit.dstSubresource.mipLevel = i; // 目标层级 blit.dstOffsets[1] = {mipWidth/2, mipHeight/2, 1}; // 目标尺寸减半
- 执行 blit 命令:使用
vkCmdBlitImage
复制并缩放数据,过滤方式为VK_FILTER_LINEAR
。 - 转换源层级为渲染可用:转为
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
。
- 转换源层级布局:将
- 处理最后一个层级:单独转换为渲染布局。
- 循环处理每个 mip 级别(从 1 开始):
-
关键代码逻辑:
for (uint32_t i = 1; i < mipLevels; i++) {// 布局转换与 blit 操作...if (mipWidth > 1) mipWidth /= 2;if (mipHeight > 1) mipHeight /= 2; }
四、线性滤波支持检查
-
硬件支持验证:
VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties); if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {throw std::runtime_error("不支持线性滤波!"); }
-
备选方案:
- 搜索支持线性滤波的格式。
- 使用
stb_image_resize
等库在软件中生成 mipmap。
五、采样器配置
-
关键参数:
mipmapMode
:VK_SAMPLER_MIPMAP_MODE_NEAREST
:直接选择层级采样。VK_SAMPLER_MIPMAP_MODE_LINEAR
:混合相邻层级采样。
minLod/maxLod
:控制采样的 lod 范围,VK_LOD_CLAMP_NONE
表示使用所有层级。mipLodBias
:偏移 lod 值,调整采样层级。
-
典型配置示例:
VkSamplerCreateInfo samplerInfo{}; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.mipLodBias = 0.0f;
六、效果与实践建议
- 视觉效果:消除莫尔条纹,文字边缘更平滑。
- 性能优化:预先生成 mipmap 可避免运行时计算,通常存储于纹理文件中。
- 调试建议:修改
minLod
可强制使用特定层级,观察不同 LOD 效果。- 设置
samplerInfo.minLod = static_cast<float>(texture.mipLevels / 2);
效果:
- 设置
samplerInfo.minLod = static_cast<float>(texture.mipLevels / 4);
效果:
- 默认
samplerInfo.minLod = 0.0f;
效果:
- 设置
多重采样抗锯齿(MSAA)总结
一、MSAA基础概念
- 作用:解决几何图形边缘锯齿状走样问题,通过每个像素使用多个采样点计算最终颜色。
- 原理:普通渲染使用单个采样点,MSAA使用多个采样点(如2/4/8倍),采样数越多效果越好但性能开销越大。
- 核心流程:在屏幕外多重采样缓冲中渲染,再解析到常规帧缓冲。
二、获取硬件支持的采样计数
- 实现函数:
VkSampleCountFlagBits getMaxUsableSampleCount() {VkPhysicalDeviceProperties props;vkGetPhysicalDeviceProperties(physicalDevice, &props);// 取颜色和深度采样计数的交集VkSampleCountFlags counts = props.limits.framebufferColorSampleCounts & props.limits.framebufferDepthSampleCounts;// 按优先级返回最高支持的采样数if (counts & VK_SAMPLE_COUNT_64_BIT) return VK_SAMPLE_COUNT_64_BIT;// 依次检查32/16/8/4/2/1位采样return VK_SAMPLE_COUNT_1_BIT; }
- 应用场景:在物理设备选择时调用,设置类成员
msaaSamples
。
三、渲染目标设置
- 创建多重采样颜色缓冲:
void createColorResources() {VkFormat format = swapChainImageFormat;// 关键参数:采样数msaaSamples,禁用mipmap(1级)createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, colorImage, colorImageMemory);colorImageView = createImageView(colorImage, format, VK_IMAGE_ASPECT_COLOR_BIT, 1); }
- 深度缓冲同步修改:
void createDepthResources() {// 同步更新深度缓冲的采样数createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, ...); }
- 资源清理与重建:
- 在
cleanupSwapChain
中释放colorImage
相关资源。 - 在
recreateSwapChain
中重新创建颜色和深度资源。
- 在
四、渲染通道与附件配置
- 修改颜色/深度附件:
colorAttachment.samples = msaaSamples; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 不再直接呈现 depthAttachment.samples = msaaSamples;
- 添加解析附件:
VkAttachmentDescription colorAttachmentResolve{}; colorAttachmentResolve.format = swapChainImageFormat; colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; // 解析到单采样常规图像 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // 用于呈现
- 子通道与依赖关系:
VkAttachmentReference colorAttachmentResolveRef{2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; subpass.pResolveAttachments = &colorAttachmentResolveRef; // 启用解析操作 // 更新依赖关系以确保写入完成 dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
- 帧缓冲与管线配置:
// 帧缓冲附件包含多重采样颜色、深度和解析目标 std::array<VkImageView, 3> attachments = {colorImageView, depthImageView, swapChainImageViews[i]}; // 管线启用多重采样 multisampling.rasterizationSamples = msaaSamples;
五、质量优化:采样着色(Sample Shading)
- 作用:解决纹理内部着色混叠,补充MSAA仅平滑边缘的不足。
- 实现步骤:
- 设备功能启用:
deviceFeatures.sampleRateShading = VK_TRUE;
- 管线配置:
multisampling.sampleShadingEnable = VK_TRUE; multisampling.minSampleShading = 0.2f; // 采样着色阈值,越接近1越平滑
- 设备功能启用:
六、关键注意事项
- 性能权衡:高采样数(如64x)对性能影响显著,建议根据设备能力动态调整。
- 解析必要性:多重采样图像无法直接呈现,必须通过解析附件转换为单采样图像。
- 兼容性检查:部分旧设备可能不支持高采样数,需做好降级处理。
- 视觉效果:MSAA主要改善几何边缘平滑度,纹理混叠需结合采样着色或纹理过滤处理。
效果
-
MSAA = VK_SAMPLE_COUNT_1_BIT
-
MSAA = getMaxUsableSampleCount()
当前代码分支:13_mipmap_msaa