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

Vulkan 学习笔记15—Mipmap 与多重采样

一、Mipmap 生成总结

一、Mipmap 基础概念
  • 定义:Mipmap 是图像预先计算的缩小版本,每个层级宽高为前一层的一半,用作细节级别(LOD)。
  • 作用
    • 远离相机的对象使用较小层级采样,提升渲染速度。
    • 避免莫尔条纹等伪影。
  • 存储方式:在 Vulkan 中存储于 VkImage 的不同 mip 级别,级别 0 为原始图像,后续层级构成 mip 链。
二、图像创建与 mip 级别计算
  1. 计算 mip 级别数

    mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
    
    • 原理:通过最大维度的对数计算可被 2 整除的次数,加 1 包含原始图像。
  2. 修改关键函数

    • createImage:添加 mipLevels 参数,设置 imageInfo.mipLevels
    • createImageView:设置 viewInfo.subresourceRange.levelCountmipLevels
    • transitionImageLayout:设置 barrier.subresourceRange.levelCountmipLevels
  3. 函数调用更新示例

    // 纹理图像创建(含 mipLevels)
    createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, ...);
    // 纹理图像视图创建
    textureImageView = createImageView(textureImage, ..., mipLevels);
    
三、Mipmap 生成流程
  1. 准备工作

    • 为纹理图像添加 VK_IMAGE_USAGE_TRANSFER_SRC_BIT 使用标志。
    • 确保图像布局为 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  2. 核心函数 generateMipmaps

    • 循环处理每个 mip 级别(从 1 开始)
      1. 转换源层级布局:将 i-1 层级转为 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
      2. 设置 blit 操作
        VkImageBlit blit{};
        blit.srcSubresource.mipLevel = i - 1;  // 源层级
        blit.dstSubresource.mipLevel = i;      // 目标层级
        blit.dstOffsets[1] = {mipWidth/2, mipHeight/2, 1};  // 目标尺寸减半
        
      3. 执行 blit 命令:使用 vkCmdBlitImage 复制并缩放数据,过滤方式为 VK_FILTER_LINEAR
      4. 转换源层级为渲染可用:转为 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
    • 处理最后一个层级:单独转换为渲染布局。
  3. 关键代码逻辑

    for (uint32_t i = 1; i < mipLevels; i++) {// 布局转换与 blit 操作...if (mipWidth > 1) mipWidth /= 2;if (mipHeight > 1) mipHeight /= 2;
    }
    
四、线性滤波支持检查
  1. 硬件支持验证

    VkFormatProperties formatProperties;
    vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);
    if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {throw std::runtime_error("不支持线性滤波!");
    }
    
  2. 备选方案

    • 搜索支持线性滤波的格式。
    • 使用 stb_image_resize 等库在软件中生成 mipmap。
五、采样器配置
  1. 关键参数

    • mipmapMode
      • VK_SAMPLER_MIPMAP_MODE_NEAREST:直接选择层级采样。
      • VK_SAMPLER_MIPMAP_MODE_LINEAR:混合相邻层级采样。
    • minLod/maxLod:控制采样的 lod 范围,VK_LOD_CLAMP_NONE 表示使用所有层级。
    • mipLodBias:偏移 lod 值,调整采样层级。
  2. 典型配置示例

    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倍),采样数越多效果越好但性能开销越大。
  • 核心流程:在屏幕外多重采样缓冲中渲染,再解析到常规帧缓冲。
二、获取硬件支持的采样计数
  1. 实现函数
    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;
    }
    
  2. 应用场景:在物理设备选择时调用,设置类成员msaaSamples
三、渲染目标设置
  1. 创建多重采样颜色缓冲
    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);
    }
    
  2. 深度缓冲同步修改
    void createDepthResources() {// 同步更新深度缓冲的采样数createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, ...);
    }
    
  3. 资源清理与重建
    • cleanupSwapChain中释放colorImage相关资源。
    • recreateSwapChain中重新创建颜色和深度资源。
四、渲染通道与附件配置
  1. 修改颜色/深度附件
    colorAttachment.samples = msaaSamples;
    colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // 不再直接呈现
    depthAttachment.samples = msaaSamples;
    
  2. 添加解析附件
    VkAttachmentDescription colorAttachmentResolve{};
    colorAttachmentResolve.format = swapChainImageFormat;
    colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; // 解析到单采样常规图像
    colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // 用于呈现
    
  3. 子通道与依赖关系
    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;
    
  4. 帧缓冲与管线配置
    // 帧缓冲附件包含多重采样颜色、深度和解析目标
    std::array<VkImageView, 3> attachments = {colorImageView, depthImageView, swapChainImageViews[i]};
    // 管线启用多重采样
    multisampling.rasterizationSamples = msaaSamples;
    
五、质量优化:采样着色(Sample Shading)
  1. 作用:解决纹理内部着色混叠,补充MSAA仅平滑边缘的不足。
  2. 实现步骤
    • 设备功能启用:
      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

http://www.lryc.cn/news/573938.html

相关文章:

  • 【webSocket】WebSocket全双工通信实战指南
  • 从零开始手写redis(15)实现自己的 HashMap
  • java专题漏洞总结 + 靶场练习
  • 【学习笔记】深入理解Java虚拟机学习笔记——第10章 前端编译与优化
  • RA4M2开发IOT(10)----集成LPS22DF气压计
  • 扫雷中的数学原理
  • AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年6月22日第116弹
  • 64-Oracle Redo Log
  • 肖臻《区块链技术与应用》第六讲:比特币网络
  • 点点(小红书AI搜索):生活场景的智能搜索助手
  • 数据库(1)-SQL
  • C++ - 标准库之 <string> npos(npos 概述、npos 的作用)
  • 2140、解决智力问题
  • 用 Python 绘制动态方块热力图:从数据到可视化的完美蜕变
  • Java基础复习之接口
  • PyTorch 入门学习笔记
  • LLM存储优化:大量长对话解决方案
  • 解决OSS存储桶未创建导致的XML错误
  • SQL Server基础语句4:数据定义
  • C#设计模式-Builder-生成器-对象创建型模式
  • JMeter API 并发性能测试计划JMX文件解析
  • 【力扣 中等 C】983. 最低票价
  • 通义大模型在文档自动化处理中的高效部署指南(OCR集成与批量处理优化)
  • Flink SourceFunction深度解析:数据输入的起点与奥秘
  • OpenAI 如何将 Kubernetes 扩展到了 7500 个节点
  • 46- 赎金信
  • 如何仅用AI开发完整的小程序<3>—创建小程序基础框架
  • python案例练习
  • 《单光子成像》第八章 预习2025.6.22
  • 零基础学习Redis(14) -- Spring中使用Redis