操作系統(tǒng):Windows8.1
顯卡:Nivida GTX965M
開發(fā)工具:Visual Studio 2017
在本章節(jié)我們將為圖形管線創(chuàng)建另外兩個資源來對圖像進行采樣。第一個資源我們之前已經(jīng)接觸過了,就是交換鏈,但是第二個資源比較新,它涉及著色器如何從圖像中讀取紋素。
Texture image view
我們之前看過交換鏈和幀緩沖區(qū),通過圖像視圖而不是字節(jié)訪問圖像。我們也會借助圖像視圖來訪問貼圖圖像。
添加一個類成員變量vkImageView保存貼圖圖像,并且創(chuàng)建新的函數(shù)createTextureImageView:
VkImageView textureImageView; ...void initVulkan() { ... createTextureImage(); createTextureImageView(); createVertexBuffer(); ... } ...void createTextureImageView() { }
函數(shù)中的代碼可以主要基于createImageViews。僅有的兩個變化是format和image字段:
VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = textureImage; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1;
這里已經(jīng)省略了顯示的viewInfo.components初始化,因為VK_COMPONET_SWIZZLE_IDENTITY被定義為0。最后在函數(shù)中通過調(diào)用vkCreateImageView完成圖像視圖的創(chuàng)建:
if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); }
因為很多邏輯都是從createImageViews復(fù)制過來的,所以可以抽象一個新的函數(shù)createImageView封裝該部分邏輯。
VkImageView createImageView(VkImage image, VkFormat format) { VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = format; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; VkImageView imageView; if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { throw std::runtime_error("failed to create texture image view!"); } return imageView; }
createTextureImageView函數(shù)可以簡化為:
void createTextureImageView() { textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM); }
createImageViews可以簡化為:
void createImageViews() { swapChainImageViews.resize(swapChainImages.size()); for (uint32_t i = 0; i < swapChainImages.size(); i++) { swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat); } }
確保程序退出的時候銷毀圖像視圖,并在銷毀圖像本身前清理圖像視圖。
Samplers
著色器直接從圖像中讀取紋素是可以的,但是當(dāng)它們作為貼圖紋理的時候并不常見。貼圖紋理通常使用采樣器來訪問,這以為著使用過濾器和變換來計算最終的顏色。
這些過濾器有助于處理超載采樣的問題??紤]一個映射到幾何圖形的紋理貼圖,擁有比紋素更多的片元。如果只是在每個片段中使用最接近的紋理坐標(biāo),那么會獲得第一個圖像的結(jié)果:
如果混合最近的四個紋素通過顯性插值,我們會看到更加平滑的結(jié)果,如右圖所示。當(dāng)然,應(yīng)用程序可能具有符合左側(cè)風(fēng)格的藝術(shù)要求(比如Minecraft),但是常規(guī)的圖形應(yīng)用程序中更傾向右側(cè)的效果。當(dāng)從紋理中讀取一個顏色的時候,采樣器自動應(yīng)用過濾器。
如果采樣負載采樣也會造成問題。當(dāng)采樣頻率過高的時候,比如對于棋盤的紋理進行采樣,會導(dǎo)致在有銳度角的地方產(chǎn)生幻影。
如作左側(cè)圖示,順著距離的變化,紋理變的模糊且混亂的。解決方案是各向異性過濾 anisotropic filtering,它會自動被采樣器應(yīng)用。
除了這些過濾器,采樣器也關(guān)注變換。當(dāng)嘗試讀取圖像外的紋素的時候,采用什么尋址模式。下圖顯示了一些可能的模式:
添加新函數(shù)createTextureSampler配置采樣器對象。我們稍后會使用它從著色器中讀取顏色。
void initVulkan() { ... createTextureImage(); createTextureImageView(); createTextureSampler(); ... } ...void createTextureSampler() { }
采樣器通過VkSamplerCreateInfo結(jié)構(gòu)體配置,它用來指定將要應(yīng)用的過濾器和變換。
VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR;
magFilter和minFilter過濾器字段指定紋素放大和縮小內(nèi)插值方式。放大關(guān)注上文描述的超采樣問題,縮小關(guān)注負載采樣的問題。VK_FILTER_NEAREST和VK_FILTER_LINEAR是可選的選項,對應(yīng)上面圖片紕漏的模式。
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
可以使用addressMode字段指定每個軸向使用的尋址模式。有效的值列在下方。大多數(shù)在圖像中已經(jīng)解釋說明過了。需要注意的是軸向在這里稱為 U,V 和 W 代替 X,Y 和 Z。這是紋理空間坐標(biāo)的約定。
VK_SAMPLER_ADDRESS_MODE_REPEAT:當(dāng)超過圖像尺寸的時候采用循環(huán)填充。
VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:與循環(huán)模式類似,但是當(dāng)超過圖像尺寸的時候,它采用反向鏡像效果。
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:當(dāng)超過圖像尺寸的時候,采用邊緣最近的顏色進行填充。
VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TOEDGE:與邊緣模式類似,但是使用與最近邊緣相反的邊緣進行填充。
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:當(dāng)采樣超過圖像的尺寸時,返回一個純色填充。
在這里使用什么樣的尋址模式并不重要,因為我們不會在圖像之外進行采樣。但是循環(huán)模式是普遍使用的一種模式,因為它可以用來實現(xiàn)諸如瓦片地面和墻面的紋理效果。
samplerInfo.anisotropyEnable = VK_TRUE; samplerInfo.maxAnisotropy = 16;
這兩個字段指定是否使用各向異性過濾器。沒有理由不使用該特性,除非性能是一個問題。maxAnisotropy字段限制可用于計算最終顏色的紋素采樣的數(shù)量。低的數(shù)值會得到比較好的性能,但是回得到較差的質(zhì)量。當(dāng)前沒有任何的圖形硬件可以使用查過16個采樣器,因為與其超過16個采樣器之間的差異可以忽略不計。
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
borderColor字段指定采樣范圍超過圖像時候返回的顏色,與之對應(yīng)的是邊緣尋址模式??梢砸詅loat或者int格式返回黑色,白色或者透明度。但是不能指定任意顏色。
samplerInfo.unnormalizedCoordinates = VK_FALSE;
unnormalizedCoordinates字段指定使用的坐標(biāo)系統(tǒng),用于訪問圖像的紋素。如果字段為VK_TRUE,意味著可以簡單的使用坐標(biāo)范圍為 [ 0, texWidth ) 和 [ 0, texHeight )。如果使用VK_FALSE,意味著每個軸向紋素訪問使用 [ 0, 1) 范圍。真實的應(yīng)用程序總是使用歸一化的坐標(biāo)。因為這樣可以使用完全相同坐標(biāo)的不同分辨率的紋理。
samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
如果開啟比較功能,那么紋素首先和值進行比較,并且比較后的值用于過濾操作。主要用在陰影紋理映射的 percentage-closer filtering 即百分比近似過濾器。我們會在未來的章節(jié)中看到。
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = 0.0f;
所有這些字段應(yīng)用在mipmapping。mipmapping也在未來章節(jié)中看到,但是基本的它可以應(yīng)用另一種濾波器。
采樣器的功能現(xiàn)在已經(jīng)完整的定義了。添加類成員持有采樣器對象的引用并通過vkCreateSampler創(chuàng)建采樣器:
VkImageView textureImageView; VkSampler textureSampler; ...void createTextureSampler() { ... if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) { throw std::runtime_error("failed to create texture sampler!"); } }
需要注意的是采樣器沒有任何地方引用VkImage。采樣器是一個獨特的對象,它提供了從紋理中提取顏色的接口。它可以應(yīng)用在任何你期望的圖像中,無論是1D,2D,或者是3D。也與之前很多舊的API是不同的,后者將紋理圖像與過濾器混合成單一狀態(tài)。
在程序的最后且不再訪問圖像的時候,銷毀采樣器:
void cleanup() { cleanupSwapChain(); vkDestroySampler(device, textureSampler, nullptr); vkDestroyImageView(device, textureImageView, nullptr); ... }
Anisotropy device feature
如果現(xiàn)在運行程序,你會看到validation layer消息如下:
這是因為各向異性濾波器是一個可選的特性。我們需要更新createLogicalDevice函數(shù)請求它:
VkPhysicalDeviceFeatures deviceFeatures = {}; deviceFeatures.samplerAnisotropy = VK_TRUE;
并且盡管現(xiàn)在的圖形卡不太可能不支持該功能,但建議仍然更新isDeviceSuitable函數(shù)去檢測是否有效:
bool isDeviceSuitable(VkPhysicalDevice device) { ... VkPhysicalDeviceFeatures supportedFeatures; vkGetPhysicalDeviceFeatures(device, &supportedFeatures); return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy; }
vkGetPhysicalDeviceFeatures將VkPhysicalDeviceFeatures結(jié)構(gòu)重新定義,指定哪些特性被支持而不是通過設(shè)置boolean值來請求。
如果不是強制使用各向異性濾波器,也可以簡單的通過條件設(shè)定來不使用它:
samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1;
下一章我們將圖像與采樣器對象公開到著色器中,繪制紋理到正方形上。
http://www.cnblogs.com/heitao/p/7125118.html