操作系統(tǒng):Windows8.1
顯卡:Nivida GTX965M
開發(fā)工具:Visual Studio 2017
Introduction
到目前為止,我們所使用的幾何圖形為3D,但仍然完全扁平的。在本章節(jié)中我們添加Z坐標(biāo)到3D模型數(shù)據(jù)中。我們將使用這個第三個坐標(biāo)在當(dāng)前平面上放置一個正方形,以查看幾何圖形沒有進(jìn)行深度排序造成的問題。
3D geometry
修改 Vertex 結(jié)構(gòu)體使用3D vector作為位置,并且更新對應(yīng)VkVertexInputAttributeDescription的 format。
struct Vertex { glm::vec3 pos; glm::vec3 color; glm::vec2 texCoord; ... static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() { std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {}; attributeDescriptions[0].binding = 0; attributeDescriptions[0].location = 0; attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; attributeDescriptions[0].offset = offsetof(Vertex, pos); ... } };
下一步更新頂點(diǎn)著色器接受和轉(zhuǎn)換3D坐標(biāo)作為輸入。別忘記重新編譯它!
layout(location = 0) in vec3 inPosition; ...void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); fragColor = inColor; fragTexCoord = inTexCoord; }
最后,更新 vertices 容器包含 Z 坐標(biāo):
const std::vector<Vertex> vertices = { {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} };
如果運(yùn)行程序,會看到與之前同樣的結(jié)果。現(xiàn)在是時候添加一些額外的幾何圖形,使場景更有趣,并展示我們將在本章節(jié)中解決的問題。復(fù)制頂點(diǎn)以定義當(dāng)前正方形的位置,如下所示:
使用Z坐標(biāo) -0.5f 并且為額外的方形添加適當(dāng)?shù)乃饕?/p>
const std::vector<Vertex> vertices = { {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} };const std::vector<uint16_t> indices = { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4};
運(yùn)行程序現(xiàn)在會看到類似于Escher的例子:
問題是,下方正方形的片段被繪制在上方的片段上,這僅僅是因?yàn)樗谒饕龜?shù)組中。有兩種方式解決這種問題:
從后面到前面深入分析所有的繪圖調(diào)用
使用深度緩沖過去進(jìn)行深度測試
第一種方法通常用于繪制透明對象,因?yàn)榕c順序不管的透明度是難以解決的難題。然而,通過深度排序片段的問題通常使用深度緩沖區(qū) depth buffer來解決。深度緩沖區(qū)是一個額外的附件,用于村粗每個位置的深度,就像顏色附件存儲每個位置的顏色一樣。每次光柵化生成片段時,深度測試將檢查新片段是否比上一個片段更近。如果沒有,新的片段被丟棄。一個片段將深度測試的值寫入深度緩沖區(qū)??梢詮钠沃魈幚泶酥?,就像可以操作顏色輸出一樣。
#define GLM_FORCE_RADIANS#define GLM_FORCE_DEPTH_ZERO_TO_ONE#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>
借助GLM生產(chǎn)出的透視投影矩陣默認(rèn)使用OpenGL的深度范圍,收斂在 -1.0 到 1.0。我們需要使用GLM_FORCE_DEPTH_ZERO_TO_ONE定義將其配置為使用 0.0 到 1.0 的Vulkan深度范圍。
Depth image and view
深度附件是基于圖像的,就像顏色附件。所不同的是交換鏈不會自動創(chuàng)建深度圖像。我們僅需要一個深度圖像,因?yàn)槊看沃挥幸粋€繪制操作。深度圖像再次需要申請三種資源:圖像,內(nèi)存和圖像視圖。
VkImage depthImage; VkDeviceMemory depthImageMemory; VkImageView depthImageView;
創(chuàng)建createDepthResources函數(shù)來配置資源:
void initVulkan() { ... createCommandPool(); createDepthResources(); createTextureImage(); ... } ...void createDepthResources() { }
創(chuàng)建深度圖像非常直接。它具備與顏色附件同樣的分辨率,定義交換鏈尺寸,合理的深度圖像是否方式,最佳的平鋪和設(shè)備本地內(nèi)存。唯一的問題是:對于深度圖像什么是正確的格式?format必須包含深度原件,諸如 VK_FORMAT 中的 _D??_。
不像紋理貼圖,我們不一定需要特定的格式,因?yàn)槲覀儾粫苯訌某绦蛑性L問紋素。它僅僅需要一個合理的準(zhǔn)確性,至少24位在實(shí)際程序中是常見的。有幾種符合要求的格式:
VK_FORMAT_D32_SFLOAT: 32-bit float depth
VK_FORMAT_D32_SFLOAT_S8_UNIT: 32-bit signed float depth 和 8-bit stencil component
VK_FORMAT_D32_UNORM_S8_UINT: 24-bit float depth 和 8-bit stencil component
stencil component 模版組件用于模版測試 stencil tests,這是可以與深度測試組合的附加測試。我們將在未來的章節(jié)中展開。
我們可以簡化為 VK_FORMAT_D32_SFLOAT 格式,因?yàn)樗闹С质欠浅3R姷?,但是盡可能的添加一些額外的靈活性也是很好的。我們增加一個函數(shù) findSupportedFormat 從候選格式列表中 根據(jù)期望值的降序原則,檢測第一個得到支持的格式。
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { }
支持的格式依賴于所使用的 tiling mode平鋪模式和具體的用法,所以我們必須包含這些參數(shù)??梢允褂?nbsp;vkGetPhysicalDeviceFormatProperties 函數(shù)查詢格式的支持:
for (VkFormat format : candidates) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); }
VkFormatProperties 結(jié)構(gòu)體包含三個字段:
linearTilingFeatures: 使用線性平鋪格式
optimalTilingFeatures: 使用最佳平鋪格式
bufferFeatures: 支持緩沖區(qū)
只有前兩個在這里是相關(guān)的,我們檢查取決于函數(shù)的 tiling 平鋪參數(shù)。
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; }
如果沒有任何期望的格式得到支持,我們可以指定一個特殊的值或者拋出異常:
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("failed to find supported format!"); }
我們添加 findDepthFormat 輔助函數(shù), 以選擇具有深度組件的格式,該深度組件支持使用深度附件:
VkFormat findDepthFormat() { return findSupportedFormat( {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT ); }
確保使用 VK_FORMAT_FEATURE_ 標(biāo)志代替 VK_IMAGE_USAGE_ 。所有的候選格式都包含深度組件,但是最后兩個也包含 stencil 組件。我們不會使用它,但是我們需要考慮到這一點(diǎn),比如在這些格式的圖像布局進(jìn)行變換的時候。添加一個簡單的輔助函數(shù),告訴我們所選擇的深度格式是否包含模版組件:
bool hasStencilComponent(VkFormat format) { return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; }
調(diào)用函數(shù)從 createDepthResources 找到深度格式:
VkFormat depthFormat = findDepthFormat();
我們現(xiàn)在擁有所有必須的信息來調(diào)用我們的 createImage 和 createImageView 輔助函數(shù):
createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory); depthImageView = createImageView(depthImage, depthFormat);
然而,createImageView 函數(shù)現(xiàn)在假定子資源始終為 VK_IMAGE_ASPECT_COLOR_BIT , 因此我們需要將該字段轉(zhuǎn)換為參數(shù):
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) { ... viewInfo.subresourceRange.aspectMask = aspectFlags; ... }
更新對此函數(shù)的所有調(diào)用,確保正確無誤:
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT); ... depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); ... textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
這就是創(chuàng)建深度圖像。我們不需要映射它或者拷貝另一個圖像,因?yàn)槲覀儠阡秩就ǖ篱_始的時候進(jìn)行清理,就像顏色附件那樣。然而,它仍然需要變換為合適的深度附件使用的布局。我們可以在渲染通道中像顏色附件那樣做,但是在這里我們選擇使用管線屏障,因?yàn)樽儞Q只會發(fā)生一次。
transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
未定義的布局可以作為初始布局,因?yàn)樯疃葓D像內(nèi)容無關(guān)緊要。我們需要在 transitionImageLayout 中更新一些邏輯使用正確的子資源:
if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; if (hasStencilComponent(format)) { barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } } else { barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; }
盡管我們不會使用模版組件,我們確實(shí)需要將其包含在深度圖像的布局變換中。
最后,添加正確的訪問掩碼:
if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } else { throw std::invalid_argument("unsupported layout transition!"); }
現(xiàn)在圖像已經(jīng)準(zhǔn)備好作為深度附件使用。
Render pass
現(xiàn)在修改 createRenderPass 函數(shù)包含深度附件。首先指定 VkAttachmentDescription。
VkAttachmentDescription depthAttachment = {}; depthAttachment.format = findDepthFormat(); depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
format 應(yīng)該與深度圖像一致。這次我們不會關(guān)心存儲深度數(shù)據(jù)(storeOp),因?yàn)槔L制完成后它不會在被使用。這可能允許硬件執(zhí)行其他的優(yōu)化。就像顏色緩沖區(qū)一樣,我們不關(guān)心之前的深度內(nèi)容,所以我們可以使用 VK_IMAGE_LAYOUT_UNDEFINED作為 initialLayout。
VkAttachmentReference depthAttachmentRef = {}; depthAttachmentRef.attachment = 1; depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
http://www.cnblogs.com/heitao/p/7152214.html