操作系統(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。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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);

        ...
    }
};

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

下一步更新頂點(diǎn)著色器接受和轉(zhuǎn)換3D坐標(biāo)作為輸入。別忘記重新編譯它!

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

layout(location = 0) in vec3 inPosition;

...void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

最后,更新 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)前正方形的位置,如下所示:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

使用Z坐標(biāo) -0.5f 并且為額外的方形添加適當(dāng)?shù)乃饕?/p>

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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};

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

運(yùn)行程序現(xiàn)在會看到類似于Escher的例子:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

問題是,下方正方形的片段被繪制在上方的片段上,這僅僅是因?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ù)來配置資源:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

void initVulkan() {
    ...
    createCommandPool();
    createDepthResources();
    createTextureImage();
    ...
}

...void createDepthResources() {

}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

創(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;
}

如果沒有任何期望的格式得到支持,我們可以指定一個特殊的值或者拋出異常:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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!");
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

我們添加 findDepthFormat 輔助函數(shù), 以選擇具有深度組件的格式,該深度組件支持使用深度附件:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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
    );
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

確保使用 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 中更新一些邏輯使用正確的子資源:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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;
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

盡管我們不會使用模版組件,我們確實(shí)需要將其包含在深度圖像的布局變換中。

 

最后,添加正確的訪問掩碼:

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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!");
}

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

現(xiàn)在圖像已經(jīng)準(zhǔn)備好作為深度附件使用。

 Render pass


現(xiàn)在修改 createRenderPass 函數(shù)包含深度附件。首先指定 VkAttachmentDescription。

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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;

萬碼學(xué)堂,電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),Java培訓(xùn),JavaEE開發(fā)培訓(xùn),青島軟件培訓(xùn),軟件工程師培訓(xùn)

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