Vulkan 学习笔记12—深度缓冲

一、3D几何体与深度问题
  1. Z坐标引入
    将2D几何体扩展为3D时,需在Vertex结构体中添加glm::vec3 pos表示三维位置,并更新顶点输入描述符格式为VK_FORMAT_R32G32B32_SFLOAT。顶点着色器需接收3D坐标并通过模型-视图-投影矩阵转换为裁剪坐标。

  2. 深度冲突问题
    当多个3D几何体未按深度排序时,后绘制的片段会覆盖先绘制的片段(如两个正方形因索引顺序导致显示错乱)。解决方案包括:

    • 按从后到前顺序排序绘制调用(适用于透明对象)
    • 使用深度测试和深度缓冲(更常用的方案)
二、深度缓冲实现
  1. 深度图像与视图创建

    • 深度附件基于图像,需手动创建图像、分配内存和视图。
    • 支持格式:
      • VK_FORMAT_D32_SFLOAT(32位浮点深度)
      • VK_FORMAT_D32_SFLOAT_S8_UINT(32位浮点深度+8位模板分量)
      • VK_FORMAT_D24_UNORM_S8_UINT(24位深度+8位模板分量)
        (模板分量用于 模板测试:与深度测试结合使用的附加测试。)
    • 通过findSupportedFormat函数查询设备支持的深度格式,优先选择包含深度模板附件功能的格式。
    VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
         for (VkFormat format : candidates) {
             VkFormatProperties props;
             vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
    
             if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
                 return format;
             } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
                 return format;
             }
         }
    
         throw std::runtime_error("没找到支持的格式!");
     }
    
  2. 图像布局转换

    • 深度图像需从VK_IMAGE_LAYOUT_UNDEFINED转换为VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
    • 转换时需处理模板分量(若存在),并设置正确的访问掩码和管线阶段。
三、渲染通道与帧缓冲区配置
  1. 渲染通道修改

    • 添加深度附件描述:设置loadOpVK_ATTACHMENT_LOAD_OP_CLEARfinalLayout为深度附件最优布局。
    • 子通道引用深度附件,通过pDepthStencilAttachment字段绑定。
    • 子通道依赖关系需包含深度测试相关的管线阶段(如VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT)。
  2. 帧缓冲区更新

    • 每个帧缓冲区需包含颜色附件和深度附件视图,确保深度图像与交换链图像分辨率一致。
四、深度测试状态配置
  1. 深度模板状态结构体

    • depthTestEnable:启用深度测试,比较新片段与深度缓冲的深度值。
    • depthWriteEnable:允许通过测试的片段更新深度缓冲。
    • depthCompareOp:设置比较操作(如VK_COMPARE_OP_LESS,表示深度值更小的片段更近)。
  2. 清除值设置

    • 深度缓冲初始清除值设为1.0(代表最远深度),通过VkClearValue数组同时指定颜色和深度清除值。
五、窗口大小调整处理
  1. 资源重建逻辑
    • recreateSwapChain函数中,窗口大小变化时重新创建深度资源(图像、视图、帧缓冲区)。
    • 清理旧资源时需释放深度图像内存和视图。
六、关键代码要点
  • GLM投影矩阵配置:使用GLM_FORCE_DEPTH_ZERO_TO_ONE宏确保生成Vulkan兼容的0.0-1.0深度范围。
  • 模板分量处理:若格式包含模板分量(如D24_UNORM_S8_UINT),布局转换和渲染通道需同时处理深度和模板方面。
  • 管线阶段依赖:深度测试发生在VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,深度写入在VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT

通过上述步骤,可在Vulkan中实现正确的深度缓冲机制,解决3D几何体的渲染顺序问题,确保场景深度关系准确。

七、主要代码更新
  • VkContext 新增成员
struct VkContext {
    ...
    VkImage depthImage;
    VkDeviceMemory depthImageMemory;
    VkImageView depthImageView;
}

  • 新增深度资源创建函数

        void createDepthResources(VkContext* vkcontext) {
            VkFormat depthFormat = findDepthFormat(vkcontext->physicalDevice);

            createImage(vkcontext->physicalDevice,
                        vkcontext->device,
                        vkcontext->swapChainExtent.width,
                        vkcontext->swapChainExtent.height,
                        depthFormat,
                        VK_IMAGE_TILING_OPTIMAL,
                        VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
                        vkcontext->depthImage,
                        vkcontext->depthImageMemory);
            vkcontext->depthImageView =
                createImageView(vkcontext->depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, vkcontext->device);
        }


在 vkInit 函数中调用

// 创建命令池
{
   ...
}

// 创建深度资源
createDepthResources(vkcontext);

// 创建帧缓冲
...

在重建交换链函数中调用

 void recreateSwapChain(VkContext* vkcontext) {
        int width = 0, height = 0;
        glfwGetFramebufferSize(vkcontext->window, &width, &height);
        while (width == 0 || height == 0) {
            glfwGetFramebufferSize(vkcontext->window, &width, &height);
            glfwWaitEvents();
        }

        vkDeviceWaitIdle(vkcontext->device);

        cleanupSwapChain(vkcontext);
        createSwapChain(vkcontext);
        createImageViews(vkcontext);
        createDepthResources(vkcontext); // 创建深度资源
        createFramebuffers(vkcontext);
    }

重新构建项目运行效果:
在这里插入图片描述

当前代码分支:10_depth_buffer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值