一、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