[譯]Vulkan教程(13)圖形管道基礎之Shader模塊

[譯]Vulkan教程(13)圖形管道基礎之Shader模塊html

Shader modules

Unlike earlier APIs, shader code in Vulkan has to be specified in a bytecode format as opposed to human-readable syntax like GLSL and HLSL. This bytecode format is called SPIR-V and is designed to be used with both Vulkan and OpenCL (both Khronos APIs). It is a format that can be used to write graphics and compute shaders, but we will focus on shaders used in Vulkan's graphics pipelines in this tutorial.ios

與從前的API不一樣,Vulkan的shader代碼必須用字節碼格式來指定-不一樣於人類可讀的語法-例如GLSL和HLSL。這個字節碼格式被稱爲SPIR-V,設計爲用於Vulkan和OpenCL(都是Khronos的API)。這個格式可用於寫圖形和計算shader,可是本教程中咱們只關注Vulkan圖形管道的shader。git

The advantage of using a bytecode format is that the compilers written by GPU vendors to turn shader code into native code are significantly less complex. The past has shown that with human-readable syntax like GLSL, some GPU vendors were rather flexible with their interpretation of the standard. If you happen to write non-trivial shaders with a GPU from one of these vendors, then you'd risk other vendor's drivers rejecting your code due to syntax errors, or worse, your shader running differently because of compiler bugs. With a straightforward bytecode format like SPIR-V that will hopefully be avoided.github

使用字節碼的優點是,GPU廠商的編譯器to轉換shader代碼爲本地代碼-複雜度顯著下降。過去的經驗標明,用人類刻度的語法-如GLSL,有的GPU廠商對標準的解讀至關的可擴展(爲所欲爲?)。若是你碰巧寫過複雜的shader-with這些廠商的GPU,那麼你得冒險-其餘廠商的驅動可能會拒絕你的代碼-因爲語法錯誤,甚至更糟,你的shader運行結果會不同-因爲編譯器bug。用直截了當的字節碼格式-如SPIR-V,這些問題有望避免。express

However, that does not mean that we need to write this bytecode by hand. Khronos has released their own vendor-independent compiler that compiles GLSL to SPIR-V. This compiler is designed to verify that your shader code is fully standards compliant and produces one SPIR-V binary that you can ship with your program. You can also include this compiler as a library to produce SPIR-V at runtime, but we won't be doing that in this tutorial. The compiler is already included with the LunarG SDK as glslangValidator.exe, so you don't need to download anything extra.編程

可是,這不等於說咱們須要手動寫字節碼了。Khronos發佈了它們的廠商無關的編譯器that將GLSL編譯爲SPIR-V。這個編譯器能夠驗證你的shader代碼是否徹底符合標準and產生你須要的SPIR-V二進制代碼。你能夠將此編譯器包含進你的庫to在運行時生成SPIR-V,可是本教程中咱們不會這樣作。這個編譯器在LunarG SDK裏有as glslangValidator.exe文件,你不需額外下載。數組

GLSL is a shading language with a C-style syntax. Programs written in it have a main function that is invoked for every object. Instead of using parameters for input and a return value as output, GLSL uses global variables to handle input and output. The language includes many features to aid in graphics programming, like built-in vector and matrix primitives. Functions for operations like cross products, matrix-vector products and reflections around a vector are included. The vector type is called vec with a number indicating the amount of elements. For example, a 3D position would be stored in a vec3. It is possible to access single components through members like .x, but it's also possible to create a new vector from multiple components at the same time. For example, the expression vec3(1.0, 2.0, 3.0).xy would result in vec2. The constructors of vectors can also take combinations of vector objects and scalar values. For example, a vec3 can be constructed with vec3(vec2(1.0, 2.0), 3.0).緩存

GLSL是類C語法的着色語言。它有個main 函數that在每一個對象中被調用。Instead of用參數做爲輸入and用返回值做爲輸出,GLSL用全局變量處理輸入輸出問題。它有不少特性來支持圖形編程,例如內置的向量和矩陣操做。函數例如叉積、點積、矩陣-向量積and圍繞向量反射等都包含在內。向量類型是vec  with一個數字-表示元素數量。例如,一個3D位置能夠保存在一個vec3中。能夠經過成員如.x來讀寫單個變量,也能夠建立新向量from多個成員。例如,表達式vec3(1.0, 2.0, 3.0).xy 會產生vec2。向量的構造函數能夠接收向量和標量的組合。例如一個vec3 能夠由vec3(vec2(1.0, 2.0), 3.0)構造出來。安全

As the previous chapter mentioned, we need to write a vertex shader and a fragment shader to get a triangle on the screen. The next two sections will cover the GLSL code of each of those and after that I'll show you how to produce two SPIR-V binaries and load them into the program.app

如前所述,咱們要寫頂點shader和Fragment shader以在屏幕上繪製三角形。下2節將介紹其GLSL代碼,以後咱們將生成SPIR-V代碼,加載它們到程序中。

Vertex shader 頂點shader

The vertex shader processes each incoming vertex. It takes its attributes, like world position, color, normal and texture coordinates as input. The output is the final position in clip coordinates and the attributes that need to be passed on to the fragment shader, like color and texture coordinates. These values will then be interpolated over the fragments by the rasterizer to produce a smooth gradient.

頂點shader處理每一個頂點。他接收頂點的屬性,例如世界座標、顏色、法線和文理座標-做爲輸入。輸出是clip空間的位置和須要傳送到Fragment shader的屬性,例如顏色和文理座標。這些值會被插值到Fragment by光柵器to生成平滑的漸變。

clip coordinate is a four dimensional vector from the vertex shader that is subsequently turned into a normalized device coordinate by dividing the whole vector by its last component. These normalized device coordinates are homogeneous coordinates that map the framebuffer to a [-1, 1] by [-1, 1] coordinate system that looks like the following:

clip座標是四維向量from頂點shader that接下來被轉換爲標準設備座標by用它的最後一個元素除整個向量。這些標準設備座標是齊次座標that將幀緩存映射到[-1, 1]by[-1, 1]座標系統that以下圖所示:

 

 

You should already be familiar with these if you have dabbled in computer graphics before. If you have used OpenGL before, then you'll notice that the sign of the Y coordinates is now flipped. The Z coordinate now uses the same range as it does in Direct3D, from 0 to 1.

你應該已經熟悉這些了if你接觸過計算機圖形。If你用過OpenGL,你會注意到Y座標的符號如今被翻轉了。Z座標如今用的範圍與在Direct3D中相同,都是從0到1。

For our first triangle we won't be applying any transformations, we'll just specify the positions of the three vertices directly as normalized device coordinates to create the following shape:

For咱們的第一個三角形,咱們不使用任何變換,咱們直接指定3個頂點的位置as標準設備座標to建立下述形狀:

 

 

We can directly output normalized device coordinates by outputting them as clip coordinates from the vertex shader with the last component set to 1. That way the division to transform clip coordinates to normalized device coordinates will not change anything.

咱們能夠直接輸出標準設備座標by在頂點shader中將clip座標的最後元素設置爲1。這樣,除法to變換clip座標到標準設備座標就不會改變任何東西。

Normally these coordinates would be stored in a vertex buffer, but creating a vertex buffer in Vulkan and filling it with data is not trivial. Therefore I've decided to postpone that until after we've had the satisfaction of seeing a triangle pop up on the screen. We're going to do something a little unorthodox in the meanwhile: include the coordinates directly inside the vertex shader. The code looks like this:

通常這些座標會被保存在頂點buffer中,可是在Vulkan中建立頂點buffer並填入數據是很繁瑣的。所以我決定推後它,until咱們有了滿意的可見三角形出如今屏幕上。同時咱們要作一些有點非正統的事:在頂點shader中寫入座標。代碼以下:

 1 #version 450
 2  
 3 vec2 positions[3] = vec2[](  4     vec2(0.0, -0.5),  5     vec2(0.5, 0.5),  6     vec2(-0.5, 0.5)  7 );  8  
 9 void main() { 10     gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 11 }

 

The main function is invoked for every vertex. The built-in gl_VertexIndex variable contains the index of the current vertex. This is usually an index into the vertex buffer, but in our case it will be an index into a hardcoded array of vertex data. The position of each vertex is accessed from the constant array in the shader and combined with dummy z and w components to produce a position in clip coordinates. The built-in variable gl_Positionfunctions as the output.

函數main 爲每一個頂點調用一次。內置變量gl_VertexIndex 包含了當前頂點的索引。通常這是一個頂點buffer的索引,但在咱們的案例中它是一個應變按摩的頂點數組的索引。每一個訂單的位置從這個常量數組中讀取,結合傻傻的zw元素to產生clip座標的位置。內置變量gl_Position是輸出。

Fragment shader

The triangle that is formed by the positions from the vertex shader fills an area on the screen with fragments. The fragment shader is invoked on these fragments to produce a color and depth for the framebuffer (or framebuffers). A simple fragment shader that outputs the color red for the entire triangle looks like this:

由頂點shader中的位置構成的三角形佔據了屏幕上的一塊區域,其上每一個點都成爲一個Fragment。Fragment shader在這些Fragment調用to產生顏色和深度值for幀緩存(1個或多個幀緩存)。一個簡單的Fragment shader that輸出紅色for整個三角形-看起來是這樣的:

#version 450 #extension GL_ARB_separate_shader_objects : enable layout(location = 0) out vec4 outColor; void main() { outColor = vec4(1.0, 0.0, 0.0, 1.0); }

 

The main function is called for every fragment just like the vertex shader main function is called for every vertex. Colors in GLSL are 4-component vectors with the R, G, B and alpha channels within the [0, 1] range. Unlikegl_Position in the vertex shader, there is no built-in variable to output a color for the current fragment. You have to specify your own output variable for each framebuffer where the layout(location = 0) modifier specifies the index of the framebuffer. The color red is written to this outColor variable that is linked to the first (and only) framebuffer at index 0.

main 函數for每一個fragment調用一次,就像頂點shader的main 函數for每一個頂點調用一次。GLSL中的顏色是4元向量withRGBA通道在[0, 1]範圍內。與頂點shader中的gl_Position不一樣,不存在內建變量to輸出顏色到當前Fragment。你必須指定你本身ID輸出變量for每一個幀緩存where修飾符layout(location = 0)指定了幀緩存的索引。紅色寫入變量outColor  that連接到第一個(也是惟一一個)幀緩存at索引0

Per-vertex colors 逐頂點的顏色

Making the entire triangle red is not very interesting, wouldn't something like the following look a lot nicer?

讓這個三角形爲紅色,並不有趣,下面這樣的圖不會更好一點嗎?

 

 

We have to make a couple of changes to both shaders to accomplish this. First off, we need to specify a distinct color for each of the three vertices. The vertex shader should now include an array with colors just like it does for positions:

咱們必須對2個shader作出改變才能實現這個。首先,咱們須要指定不一樣的顏色for每一個頂點。頂點shader如今包含一個填寫了顏色的數組,就像位置數組同樣:

vec3 colors[3] = vec3[]( vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0) );

 

Now we just need to pass these per-vertex colors to the fragment shader so it can output their interpolated values to the framebuffer. Add an output for color to the vertex shader and write to it in the main function:

如今咱們只需傳遞這些逐頂點的顏色到Fragment shader,這樣它就能夠輸出插值的顏色到幀緩存。添加一個輸出for顏色to頂點shader,在main 函數中寫入它:

layout(location = 0) out vec3 fragColor; void main() { gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); fragColor = colors[gl_VertexIndex]; }

 

Next, we need to add a matching input in the fragment shader:

交流下,咱們須要添加一個匹配的輸入in Fragment shader:

layout(location = 0) in vec3 fragColor; void main() { outColor = vec4(fragColor, 1.0); }

 

The input variable does not necessarily have to use the same name, they will be linked together using the indexes specified by the location directives. The main function has been modified to output the color along with an alpha value. As shown in the image above, the values for fragColor will be automatically interpolated for the fragments between the three vertices, resulting in a smooth gradient.

輸入變量不須要使用相同的名字,它們會被連接到一塊兒using以location 指定的索引。函數main 被修改成輸出顏色及其alpha值。如上圖所示,變量fragColor  的值會在3個頂點之間被自動插值for Fragment,致使一個平滑的漸變效果。

Compiling the shaders 編譯shader

Create a directory called shaders in the root directory of your project and store the vertex shader in a file called shader.vert and the fragment shader in a file called shader.frag in that directory. GLSL shaders don't have an official extension, but these two are commonly used to distinguish them.

建立文件夾shaders in你項目的根文件夾下,保存頂點shader到文件shader.vert ,保存Fragment shader到文件shader.frag 。GLSL shader沒有官方擴展名,可是這2個是經常使用的to區分它們。

The contents of shader.vert should be:

shader.vert 的內容應當是:

 1 #version 450
 2 #extension GL_ARB_separate_shader_objects : enable  3  
 4 layout(location = 0) out vec3 fragColor;  5  
 6 vec2 positions[3] = vec2[](  7     vec2(0.0, -0.5),  8     vec2(0.5, 0.5),  9     vec2(-0.5, 0.5) 10 ); 11  
12 vec3 colors[3] = vec3[]( 13     vec3(1.0, 0.0, 0.0), 14     vec3(0.0, 1.0, 0.0), 15     vec3(0.0, 0.0, 1.0) 16 ); 17  
18 void main() { 19     gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 20     fragColor = colors[gl_VertexIndex]; 21 }

 

And the contents of shader.frag should be:

shader.frag 的內容應當是:

 1 #version 450
 2 #extension GL_ARB_separate_shader_objects : enable  3  
 4 layout(location = 0) in vec3 fragColor;  5  
 6 layout(location = 0) out vec4 outColor;  7  
 8 void main() {  9     outColor = vec4(fragColor, 1.0); 10 }

 

We're now going to compile these into SPIR-V bytecode using the glslangValidator program.

如今咱們要編譯這些into SPIR-V字節碼-使用glslangValidator 程序。

Windows

Create a compile.bat file with the following contents:

建立compile.bat 文件,寫入以下內容:

C:/VulkanSDK/x.x.x.x/Bin32/glslangValidator.exe -V shader.vert C:/VulkanSDK/x.x.x.x/Bin32/glslangValidator.exe -V shader.frag pause

 

Replace the path to glslangValidator.exe with the path to where you installed the Vulkan SDK. Double click the file to run it.

將路徑替換爲glslangValidator.exe 的位置where你安裝了Vulkan SDK。雙擊文件to運行它。

Linux

Create a compile.sh file with the following contents:

建立compile.sh 文件,寫入以下內容:

/home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslangValidator -V shader.vert /home/user/VulkanSDK/x.x.x.x/x86_64/bin/glslangValidator -V shader.frag

 

Replace the path to glslangValidator with the path to where you installed the Vulkan SDK. Make the script executable with chmod +x compile.sh and run it.

將路徑替換爲glslangValidator.exe 的位置where你安裝了Vulkan SDK。用chmod +x compile.sh 使它可執行,運行它。

End of platform-specific instructions 尾聲of平臺相關的指導

These two commands invoke the compiler with the -V flag, which tells it to compile the GLSL source files to SPIR-V bytecode. When you run the compile script, you'll see that two SPIR-V binaries are created: vert.spv andfrag.spv. The names are automatically derived from the type of shader, but you can rename them to anything you like. You may get a warning about some missing features when compiling your shaders, but you can safely ignore that.

這2個命令調用編譯器with標誌-V,which告訴編譯器to編譯GLSL代碼to SPIR-V字節碼。當年運行編譯腳本,你將看到2個SPIR-V二進制文件:vert.spv 和frag.spv。名字是自動從shader類型繼承來的,可是你能夠重命名它們to任何你喜歡的名字。你可能收到警告about確實某些特性when編譯你的shader,可是你能夠安全地忽略它。

If your shader contains a syntax error then the compiler will tell you the line number and problem, as you would expect. Try leaving out a semicolon for example and run the compile script again. Also try running the compiler without any arguments to see what kinds of flags it supports. It can, for example, also output the bytecode into a human-readable format so you can see exactly what your shader is doing and any optimizations that have been applied at this stage.

If你的shader有如法錯誤,那麼編譯器會告訴你錯誤的行數和問題,如你所願。嘗試少寫一個分號and再次運行編譯腳本。也試試不帶參數運行編譯器to看看它支持哪些標誌。例如,它可能也輸出字節碼into一我的類刻度的格式,以便你能夠看到你的shader在作什麼and這一階段有哪些優化。

Compiling shaders on the commandline is one of the most straightforward options and it's the one that we'll use in this tutorial, but it's also possible to compile shaders directly from your own code. The Vulkan SDK includes libshaderc, which is a library to compile GLSL code to SPIR-V from within your program.

在命令行編譯shader是最直觀的方式,咱們在本教程中就這麼用,可是直接從你的代碼編譯shader也是能夠的。Vulkan SDK包含了libshaderc,它是個庫to編譯GLSL代碼到SPIR-V(在你的程序中)。

Loading a shader 加載shader

Now that we have a way of producing SPIR-V shaders, it's time to load them into our program to plug them into the graphics pipeline at some point. We'll first write a simple helper function to load the binary data from the files.

既然咱們知道如何生成SPIR-V格式的shader了,是時候加載它們到咱們的程序to將其插入圖形管道的某處。咱們先寫一個簡單的輔助函數to家族二進制數據from文件。

#include <fstream> ... static std::vector<char> readFile(const std::string& filename) { std::ifstream file(filename, std::ios::ate | std::ios::binary); if (!file.is_open()) { throw std::runtime_error("failed to open file!"); } }

 

The readFile function will read all of the bytes from the specified file and return them in a byte array managed by std::vector. We start by opening the file with two flags:

  • ate: Start reading at the end of the file
  • binary: Read the file as binary file (avoid text transformations)

函數readFile 會讀全部的字節from指定的文件,返回它們到一個字節數組-由std::vector管理。咱們打開文件with兩個標誌:

  • ate:開始從文件末尾讀。
  • binary:將文件視爲二進制文件來讀取(避免文字轉換)。

The advantage of starting to read at the end of the file is that we can use the read position to determine the size of the file and allocate a buffer:

從文件末尾開始讀的優點是,咱們能夠用讀取位置to決定文件的大小and申請buffer:

size_t fileSize = (size_t) file.tellg(); std::vector<char> buffer(fileSize);

 

After that, we can seek back to the beginning of the file and read all of the bytes at once:

以後,咱們能夠找回到文件的開始,一次讀入全部字節:

file.seekg(0); file.read(buffer.data(), fileSize);

 

And finally close the file and return the bytes:

最後關閉文件,返回字節數組:

file.close(); return buffer;

 

We'll now call this function from createGraphicsPipeline to load the bytecode of the two shaders:

咱們如今調用這個函數from createGraphicsPipeline  to加載2個shader的字節碼:

void createGraphicsPipeline() { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); }

 

Make sure that the shaders are loaded correctly by printing the size of the buffers and checking if they match the actual file size in bytes. Note that the code doesn't need to be null terminated since it's binary code and we will later be explicit about its size.

確保shader被正確地加載by打印buffer的大小and檢查if它們匹配文件的實際大小(字節)。注意,代碼不需是null結尾,由於它是二進制代碼and咱們稍後會知道其大小。

Creating shader modules 建立shader模塊

Before we can pass the code to the pipeline, we have to wrap it in a VkShaderModule object. Let's create a helper function createShaderModule to do that.

咱們傳遞代碼到pipeline以前,咱們必須封裝它爲一個VkShaderModule 對象。咱們來建立一個輔助函數createShaderModule 作這件事。

VkShaderModule createShaderModule(const std::vector<char>& code) { }

 

The function will take a buffer with the bytecode as parameter and create a VkShaderModule from it.

這個函數接收一個字節碼的數組,據此建立一個VkShaderModule 。

Creating a shader module is simple, we only need to specify a pointer to the buffer with the bytecode and the length of it. This information is specified in a VkShaderModuleCreateInfo structure. The one catch is that the size of the bytecode is specified in bytes, but the bytecode pointer is a uint32_t pointer rather than a char pointer. Therefore we will need to cast the pointer with reinterpret_cast as shown below. When you perform a cast like this, you also need to ensure that the data satisfies the alignment requirements of uint32_t. Lucky for us, the data is stored in an std::vector where the default allocator already ensures that the data satisfies the worst case alignment requirements.

建立shader模塊很簡單,咱們只需指明buffer的指針及其商都。這個信息在VkShaderModuleCreateInfo 結構體中指明。要注意的是字節碼的大小是按字節算的,可是字節碼的指針是uint32_t 類型,而不是char 類型。所以咱們須要用reinterpret_cast 轉換指針,以下所示。當你作這樣的轉換時,你也須要確保數據知足uint32_t的佈局要求。幸運的是,數據保存在std::vector  where默認的申請器已經確保了數據知足最壞的狀況。

VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());

 

The VkShaderModule can then be created with a call to vkCreateShaderModule:

而後VkShaderModule 就能夠經過調用vkCreateShaderModule來建立了:

VkShaderModule shaderModule; if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); }

 

The parameters are the same as those in previous object creation functions: the logical device, pointer to create info structure, optional pointer to custom allocators and handle output variable. The buffer with the code can be freed immediately after creating the shader module. Don't forget to return the created shader module:

參數與以前的對象建立函數相同:邏輯設備、建立信息結構體、可選申請器函數指針and輸出變量的句柄。建立了shader模塊後,buffer就能夠被釋放了。不要忘記返回建立的shader模塊:

return shaderModule;

 

Shader modules are just a thin wrapper around the shader bytecode that we've previously loaded from a file and the functions defined in it. The compilation and linking of the SPIR-V bytecode to machine code for execution by the GPU doesn't happen until the graphics pipeline is created. That means that we're allowed to destroy the shader modules again as soon as pipeline creation is finished, which is why we'll make them local variables in the createGraphicsPipeline function instead of class members:

Shader模塊只是對shader字節碼的簡單封裝。編譯和連接of SPIR-V字節碼to機器碼for GPU執行不會發生until圖形管道被建立。這意味着,一旦管道建立完成了,咱們就能夠銷燬shader模塊了,因此咱們才讓它做爲局部變量in createGraphicsPipeline 函數,而不是類的成員。

void createGraphicsPipeline() { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);

 

The cleanup should then happen at the end of the function by adding two calls to vkDestroyShaderModule. All of the remaining code in this chapter will be inserted before these lines.

清理工做應該在函數最後發生by添加2個對vkDestroyShaderModule的調用。本章剩下的代碼會被插入這些行以前。

 ... vkDestroyShaderModule(device, fragShaderModule, nullptr); vkDestroyShaderModule(device, vertShaderModule, nullptr); }

 

Shader stage creation 建立shader階段

To actually use the shaders we'll need to assign them to a specific pipeline stage through VkPipelineShaderStageCreateInfo structures as part of the actual pipeline creation process.

爲了使用這些shader,咱們須要將它們賦予一個特定的管道階段-經過VkPipelineShaderStageCreateInfo 結構體as管道建立過程的一部分。

We'll start by filling in the structure for the vertex shader, again in the createGraphicsPipeline function.

首先咱們填充這個結構體for頂點shader,再次在createGraphicsPipeline 函數中。

VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;

 

The first step, besides the obligatory sType member, is telling Vulkan in which pipeline stage the shader is going to be used. There is an enum value for each of the programmable stages described in the previous chapter.

第一步,除了必有的sType 成員,要告訴Vulkan,shader要別用到管道的哪一個階段。有個枚舉值for每一個可編程階段(以前章節介紹過)。

vertShaderStageInfo.module = vertShaderModule; vertShaderStageInfo.pName = "main";

 

The next two members specify the shader module containing the code, and the function to invoke, known as the entrypoint. That means that it's possible to combine multiple fragment shaders into a single shader module and use different entry points to differentiate between their behaviors. In this case we'll stick to the standard main, however.

就好了個成員指定shader模塊,要調用的函數,即入口點。這意味着,能夠組合多個Fragment shader進一個單獨的shader模塊,使用不一樣的入口點to區分它們的行爲。但本案例中咱們仍舊使用標準的main

There is one more (optional) member, pSpecializationInfo, which we won't be using here, but is worth discussing. It allows you to specify values for shader constants. You can use a single shader module where its behavior can be configured at pipeline creation by specifying different values for the constants used in it. This is more efficient than configuring the shader using variables at render time, because the compiler can do optimizations like eliminating if statements that depend on these values. If you don't have any constants like that, then you can set the member to nullptr, which our struct initialization does automatically.

還有個可選成員pSpecializationInfo,咱們這裏不用它,可是它值得一提。它容許你指定shader常量的值。你能夠用一個單獨的shader模塊where其行爲可在管道建立時被配置by爲常量指定不一樣的值。這更高效than在運行時用變量配置shader,由於編譯器能夠作優化例如依據這些值去掉if 語句。若是你沒有那樣的常量,那麼你能夠設置此成員爲nullptr,咱們的結構體初始化就是自動這麼作的。

Modifying the structure to suit the fragment shader is easy:

修改結構體to適應Fragment shader是簡單的:

VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fragShaderStageInfo.module = fragShaderModule; fragShaderStageInfo.pName = "main";

 

Finish by defining an array that contains these two structs, which we'll later use to reference them in the actual pipeline creation step.

最後,定義數組that包含這2個結構體,稍候咱們用於在管道建立時引用它們。

VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};

 

That's all there is to describing the programmable stages of the pipeline. In the next chapter we'll look at the fixed-function stages.

描述管道的可編程階段的內容就這些。下一章咱們將學習固定功能階段。

C++ code / Vertex shader / Fragment shader

 

相關文章
相關標籤/搜索