一、3D几何体与深度问题
-
Z坐标引入
将2D几何体扩展为3D时,需在Vertex结构体中添加glm::vec3 pos
表示三维位置,并更新顶点输入描述符格式为VK_FORMAT_R32G32B32_SFLOAT
。顶点着色器需接收3D坐标并通过模型-视图-投影矩阵转换为裁剪坐标。 -
深度冲突问题
当多个3D几何体未按深度排序时,后绘制的片段会覆盖先绘制的片段(如两个正方形因索引顺序导致显示错乱)。解决方案包括:- 按从后到前顺序排序绘制调用(适用于透明对象)
- 使用深度测试和深度缓冲(更常用的方案)
二、深度缓冲实现
-
深度图像与视图创建
- 深度附件基于图像,需手动创建图像、分配内存和视图。
- 支持格式:
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("没找到支持的格式!");}
-
图像布局转换
- 深度图像需从
VK_IMAGE_LAYOUT_UNDEFINED
转换为VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
。 - 转换时需处理模板分量(若存在),并设置正确的访问掩码和管线阶段。
- 深度图像需从
三、渲染通道与帧缓冲区配置
-
渲染通道修改
- 添加深度附件描述:设置
loadOp
为VK_ATTACHMENT_LOAD_OP_CLEAR
,finalLayout
为深度附件最优布局。 - 子通道引用深度附件,通过
pDepthStencilAttachment
字段绑定。 - 子通道依赖关系需包含深度测试相关的管线阶段(如
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
)。
- 添加深度附件描述:设置
-
帧缓冲区更新
- 每个帧缓冲区需包含颜色附件和深度附件视图,确保深度图像与交换链图像分辨率一致。
四、深度测试状态配置
-
深度模板状态结构体
depthTestEnable
:启用深度测试,比较新片段与深度缓冲的深度值。depthWriteEnable
:允许通过测试的片段更新深度缓冲。depthCompareOp
:设置比较操作(如VK_COMPARE_OP_LESS
,表示深度值更小的片段更近)。
-
清除值设置
- 深度缓冲初始清除值设为1.0(代表最远深度),通过
VkClearValue
数组同时指定颜色和深度清除值。
- 深度缓冲初始清除值设为1.0(代表最远深度),通过
五、窗口大小调整处理
- 资源重建逻辑
- 在
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