操作系統(tǒng):Windows8.1

顯卡:Nivida GTX965M

開發(fā)工具:Visual Studio 2017


Introduction

在實(shí)際產(chǎn)品的運(yùn)行環(huán)境中3D模型的數(shù)據(jù)往往共享多個(gè)三角形之間的頂點(diǎn)數(shù)據(jù)。即使繪制一些簡(jiǎn)單的圖形也是如此,比如矩形:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

繪制矩形需要兩個(gè)三角形,通常意味著我們需要6個(gè)頂點(diǎn)數(shù)據(jù)。問題是其中的兩個(gè)頂點(diǎn)會(huì)重復(fù),導(dǎo)致數(shù)據(jù)會(huì)有50%的冗余。如果更復(fù)雜的模型,該問題會(huì)更加嚴(yán)重,平均每三個(gè)三角形就會(huì)發(fā)生重復(fù)頂點(diǎn)使用的情況。解決問題的方法是使用index buffer,即索引緩沖區(qū)。

 

索引緩沖區(qū)純粹是一個(gè)指向頂點(diǎn)緩沖區(qū)的指針數(shù)組。它允許我們重排列頂點(diǎn)數(shù)據(jù),并復(fù)用多個(gè)已經(jīng)存在的頂點(diǎn)數(shù)據(jù)。上圖顯示了有一個(gè)包含四個(gè)不重復(fù)頂點(diǎn)數(shù)據(jù)的頂點(diǎn)緩沖區(qū),通過索引緩沖區(qū)將如何顯示矩形的情況。前三個(gè)索引定義右上角的三角形,最后三個(gè)索引定義了左下角的三角形。

Index buffer creation


在本章節(jié)中,為了繪制如上圖所示的矩形,我們需要修改頂點(diǎn)數(shù)據(jù)并添加索引數(shù)據(jù)。修改后的頂點(diǎn)數(shù)據(jù)分別代表矩形四個(gè)角:

const std::vector<Vertex> vertices = {
    {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
    {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
    {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
    {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
};

左上角是紅色,右上角是綠色,右下角是藍(lán)色,左下角是白色。我們添加一個(gè)新的索引數(shù)組indices代表索引緩沖區(qū)的數(shù)據(jù)內(nèi)容。它應(yīng)該匹配途中的索引來繪制右上角的三角形和左下角的三角形。

const std::vector<uint16_t> indices = {    0, 1, 2, 2, 3, 0};

根據(jù)vertices中的條目個(gè)數(shù),我們可以使用uint16_tuint32_t作為索引緩沖區(qū)類型?,F(xiàn)在我們可以使用uint16_t,因?yàn)槲覀兪褂玫莫?dú)立頂點(diǎn)數(shù)量小于65535。

 

如頂點(diǎn)數(shù)據(jù),為了使GPU可以訪問到它們,需要將索引數(shù)據(jù)上傳到緩沖區(qū)VkBuffer。定義兩個(gè)類成員保存索引緩沖區(qū)的資源:

VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;

createIndexBuffer函數(shù)與之前的createVertexBuffer函數(shù)非常類似:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

void initVulkan() {
    ...
    createVertexBuffer();
    createIndexBuffer();
    ...
}void createIndexBuffer() {
    VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();

    VkBuffer stagingBuffer;
    VkDeviceMemory stagingBufferMemory;
    createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);    void* data;
    vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
    memcpy(data, indices.data(), (size_t) bufferSize);
    vkUnmapMemory(device, stagingBufferMemory);

    createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);

    copyBuffer(stagingBuffer, indexBuffer, bufferSize);

    vkDestroyBuffer(device, stagingBuffer, nullptr);
    vkFreeMemory(device, stagingBufferMemory, nullptr);
}

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

僅有的兩個(gè)差異。bufferSize現(xiàn)在等于索引數(shù)量乘以索引類型的大小,該類型或者是uint16_t,或者是uint32_t。indexBuffer的用法需改用VK_BUFFER_USAGE_INDEX_BUFFER_BIT代替VK_BUFFER_USAGE_VERTEX_BUFFER_BIT。其他的過程是一致的。我們創(chuàng)建暫存緩沖區(qū)拷貝頂點(diǎn)數(shù)據(jù)的內(nèi)容,并最終拷貝到設(shè)備本地索引緩沖區(qū)。

 

索引緩沖區(qū)在程序退出的時(shí)候需要清理,與頂點(diǎn)緩沖區(qū)類似:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

void cleanup() {
    cleanupSwapChain();

    vkDestroyBuffer(device, indexBuffer, nullptr);
    vkFreeMemory(device, indexBufferMemory, nullptr);

    vkDestroyBuffer(device, vertexBuffer, nullptr);
    vkFreeMemory(device, vertexBufferMemory, nullptr);

    ...
}

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

 

Using an index buffer


 

使用索引緩沖區(qū)繪制需要修改createCommandBuffers函數(shù)兩個(gè)地方。首先需要綁定索引緩沖區(qū),就像之前的頂點(diǎn)緩沖區(qū)一樣。區(qū)別是現(xiàn)在僅使用一個(gè)索引緩沖區(qū)。不幸的是,不可能對(duì)每個(gè)頂點(diǎn)屬性使用不同的索引,所以即使只有一個(gè)屬性不同,我們?nèi)匀槐仨毻耆珡?fù)制頂點(diǎn)數(shù)據(jù)。

vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets);

vkCmdBindIndexBuffer(commandBuffers[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);

索引緩沖區(qū)使用vkCmdBindIndexBuffer綁定,它持有索引緩沖區(qū)作為參數(shù),還需要偏移量和索引數(shù)據(jù)的類型。如前所述,可能的類型是VK_INDEX_TYPE_UINT16VK_INDEX_TYPE_UINT32。

 

僅僅綁定索引緩沖區(qū)不會(huì)發(fā)生任何改變,我們還需要告知Vulkan在使用索引緩沖區(qū)后,對(duì)應(yīng)的繪制命令的變化。移除vkCmdDraw函數(shù),并用vkCmdDrawIndexed替換:

vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);

該函數(shù)的調(diào)用與vkCmdDraw非常類似。前兩個(gè)參數(shù)指定索引的數(shù)量和幾何instance數(shù)量。我們沒有使用instancing,所以指定1。索引數(shù)表示被傳遞到頂點(diǎn)緩沖區(qū)中的頂點(diǎn)數(shù)量。下一個(gè)參數(shù)指定索引緩沖區(qū)的偏移量,使用1將會(huì)導(dǎo)致圖形卡在第二個(gè)索引處開始讀取。倒數(shù)第二個(gè)參數(shù)指定索引緩沖區(qū)中添加的索引的偏移。最后一個(gè)參數(shù)指定instancing偏移量,我們沒有使用該特性。

 

現(xiàn)在運(yùn)行程序如下所示:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計(jì)算培訓(xùn),高端軟件開發(fā)培訓(xùn),項(xiàng)目經(jīng)理培訓(xùn)

現(xiàn)在我們已經(jīng)通過索引緩沖區(qū)復(fù)用了頂點(diǎn)數(shù)據(jù)。在未來的加載復(fù)雜的3D模型數(shù)據(jù)中,該技術(shù)會(huì)非常的重要。

 

在前一章提到,為了更優(yōu)的分配使用資源,推薦在單個(gè)內(nèi)存中分配多個(gè)資源,如緩沖區(qū),但是實(shí)際上,我們應(yīng)該更進(jìn)一步細(xì)化。來自Nvidia的驅(qū)動(dòng)程序開發(fā)者建議將多個(gè)緩沖區(qū)(頂點(diǎn)緩沖區(qū)、索引緩沖區(qū))存儲(chǔ)到單個(gè)VkBuffer中。并在諸如vkCmdBindVertexBuffers之類的命令中使用偏移量。優(yōu)點(diǎn)在于,在這種情況下,數(shù)據(jù)會(huì)更加充分的利用緩存,因?yàn)樗鼈兣帕性谝粔K區(qū)域。甚至在同一個(gè)渲染操作中可以復(fù)用來自相同內(nèi)存塊的多個(gè)資源塊,只要刷新數(shù)據(jù)即可。該技巧稱為稱為aliasing,一些Vulkan函數(shù)有明確的標(biāo)志指定這樣做的意圖。

 

http://www.cnblogs.com/heitao/p/7073948.html