入門:調試Vulkan Samples(九)——Shader
Shader
Shader是在GPU上執行的著色程序。一般有兩種shader類型:頂點著色器和片段著色器。
頂點著色器負責處理傳入的每個頂點,輸入為頂點的屬性,比如模型空間坐標、法線和uv等,輸出為裁剪空間坐標、頂點光照顏色等。
片段作色器負責處理光柵化後的每個片段,輸入為頂點著色器的插值化輸出,輸出為光照計算後的顏色,輸出的顏色值在經過深度剔除、混合等操作後寫入frame buffer。
上述只是簡單描述了一般情況,其他,比如還有geometry、tessellation類型的shader應用於管線的不同階段;比如uniform全局變數可作為著色器的額外輸入;比如延遲渲染片段著色器除了輸出顏色,還輸出深度、法線信息。等等……
生成 SPIR-V
在Vulkan中,shader為SPIR-V格式的位元組碼,位元組碼的好處是簡化生成機器碼的編譯複雜度,但是位元組碼可讀性差,而且我們習慣於GLSL這種C語言語法的文本代碼,所以Vulkan內置了轉換GLSL碼到SPIR-V碼的功能,轉換有預編譯和運行時兩種方式。
預編譯使用包含在LunarG SDK中的glslangValidator.exe來離線生成SPIR-V文件,轉換操作類似為:
C:/VulkanSDK/1.0.17.0/Bin32/glslangValidator.exe -V shader.vert
C:/VulkanSDK/1.0.17.0/Bin32/glslangValidator.exe -V shader.frag
運行時轉換使用glslang運行時庫,轉換操作在C++代碼中調用介面:
init_glslang();
retVal = GLSLtoSPV(VK_SHADER_STAGE_VERTEX_BIT, vertShaderText, vtx_spv);
retVal = GLSLtoSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fragShaderText, frag_spv);
本示例用的是運行時生成的方式,實際項目一般使用離線生成的方式。
離線方式生成的文件需要額外的載入操作。不過shader文件一般都是當資源文件需要載入,示例中把glsl代碼以字元串的格式寫在代碼里,省略了載入glsl文本文件的步驟。
創建Shader Module
SPIR-V生成好之後需要封裝成shader module。本示例為頂點和片段兩個spv分別創建對應module對象:
VkShaderModuleCreateInfo moduleCreateInfo;
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleCreateInfo.pNext = NULL;
moduleCreateInfo.flags = 0;
moduleCreateInfo.codeSize = vtx_spv.size() * sizeof(unsigned int);
moduleCreateInfo.pCode = vtx_spv.data();
res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[0].module);
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleCreateInfo.pNext = NULL;
moduleCreateInfo.flags = 0;
moduleCreateInfo.codeSize = frag_spv.size() * sizeof(unsigned int);
moduleCreateInfo.pCode = frag_spv.data();
res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[1].module);
創建Shader Stage
Shader stage是渲染管線(pipeline)配置的一部分,創建好的shader module對象用於初始化VkPipelineShaderStageCreateInfo的module變數。
info.shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.shaderStages[0].pNext = NULL;
info.shaderStages[0].pSpecializationInfo = NULL;
info.shaderStages[0].flags = 0;
info.shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
info.shaderStages[0].pName = "main";
info.shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.shaderStages[1].pNext = NULL;
info.shaderStages[1].pSpecializationInfo = NULL;
info.shaderStages[1].flags = 0;
info.shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
info.shaderStages[1].pName = "main";
代碼中用stage 0表示vertex 階段,stage 1為fragment 階段。
類似VkPipelineXXXCreateInfo的實例化對象都會是最終pipeline配置的一部份。
推薦閱讀:
