操作系統(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)單的圖形也是如此,比如矩形:
繪制矩形需要兩個(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_t或uint32_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ù)非常類似:
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); }
僅有的兩個(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ū)類似:
void cleanup() { cleanupSwapChain(); vkDestroyBuffer(device, indexBuffer, nullptr); vkFreeMemory(device, indexBufferMemory, nullptr); vkDestroyBuffer(device, vertexBuffer, nullptr); vkFreeMemory(device, vertexBufferMemory, nullptr); ... }
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_UINT16和VK_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)行程序如下所示:
現(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