Passing Data from Stage to Stage(在渲染管线各个阶段之间传递数据)
So far, you have seen how to pass data into a vertex shader by creating a vertex attribute using the in keyword(到现在为止,你已经看到了如何通过vertex attribute给vertex shader传递数据了), how to communicate with fixed-function blocks by reading and writing built-in variables such as gl_VertexID and gl_Position,(如何利用固定功能通过读写内置的诸如gl_VertexID和gl_Position进行通信呢?) and how to output data from the fragment shader using the out keyword(如何通过fragment shader使用out修饰符输出数据呢). However, it's also possible to send your own data from shader stage to shader stage using the same in and out keywords(当然,你也可以使用in和out在渲染管线的各个阶段之间传递自己的数据). Just as you used the out keyword in the fragment shader to create the output variable to which it writes its color values(就如同你之前使用过的,使用out关键字就能让fragment shader输出颜色), so you can also create an output variable in the vertex shader by using the out keyword(你也可以在vertex shader中使用out关键字让vertex shader输出数据). Anything you write to an output variable in one shader is sent to a similarly named variable declared with the in keyword in the subsequent stage(你在使用out修饰的输出的变量中写入的数据,都会传递到渲染管线的下一个阶段中同名的用in修饰的变量中). For example, if your vertex shader declares a variable called vs_color using the out keyword(比如,你用out修饰了一个叫vs_color的变量), it would match up with a variable named vs_color declared with the in keyword in the fragment shader stage(这将会与在fragment shader中使用in修饰的vs_color变量配对成功) (assuming no other stages were active in between(假设vertex shader和fragment shader之间没有其他的shader))
If we modify our simple vertex shader as shown in Listing 3.3 to include vs_color as an output variable(如果我们按照Listing3.3所示修改我们的vertex shader,将vs_color作为我们的输出数据), and correspondingly modify our simple fragment shader to includevs_color as an input variable as shown in Listing 3.4,(并且对应的Listing3.4所示在fragment shader中申明变量vs_color作为输入数据) we can pass a value from the vertex shader to the fragment shader(我们可以将一个变量从vertex shader传递到fragment shader). Then, rather than outputting a hard-coded value(相比于硬编码一个数据),the fragment can simply output the color passed to it from the vertex shader(现在的fragment shader可以输出一个从vertex shader那边传过来的颜色了)
#version 450 core
// 'offset' and 'color' are input vertex attributes
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// 'vs_color' is an output that will be sent to the next shader stage
out vec4 vs_color;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4(0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4(0.25, 0.25, 0.5, 1.0));
// Add 'offset' to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_color = color;
}
Listing 3.3: Vertex shader with an output
As you can see in Listing 3.3, we declare a second input to our vertex shader, color (this time at location 1), and write its value to the vs_output output. This is picked up by the fragment shader of Listing 3.4 and written to the framebuffer. This allows us to pass a color all the way from a vertex attribute that we can set with glVertexAttrib* () through the vertex shader, into the fragment shader, and out to the framebuffer. As a consequence, we can draw different-colored triangles!
#version 450 core
// Input from the vertex shader
in vec4 vs_color;
// Output to the framebuffer
out vec4 color;
void main(void)
{
// Simply assign the color we were given by the vertex shader to our output
color = vs_color;
}
Listing 3.4: Fragment shader with an input
Interface Blocks
Declaring interface variables one at a time is possibly the simplest way to communicate data between shader stages(一次定义一个变量或许是最简单的渲染管线各阶段通信的方式). However, in most nontrivial applications(然而在很多程序中), you will likely want to communicate a number of different pieces of data between stages(你会传递更多的数据); these may include arrays, structures, and other complex arrangements of variables(这些数据可能就包括数组、结构体以及一些其他的复杂的数据). To achieve this, we can group together a number of variables into an interface block(为了实现这一目标,我们可以把很多个变量组织到一个数据块中去). The declaration of an interface block looks a lot like a structure declaration(数据块的申明跟结构体的申明类似), except that it is declared using the in or out keyword depending on whether it is an input to or output from the shader(除了数据块使用in和out关键字来区分数据块是输入数据还是输出数据这一点). An example interface block definition is shown in Listing 3.5(一个数据块的例子就如同Listing3.5的代码所示)
#version 450 core
// 'offset' is an input vertex attribute
layout (location = 0) in vec4 offset;
layout (location = 1) in vec4 color;
// Declare VS_OUT as an output interface block
out VS_OUT
{
vec4 color; // Send color to the next stage
} vs_out;
void main(void)
{
const vec4 vertices[3] = vec4[3](vec4(0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4(0.25, 0.25, 0.5, 1.0));
// Add 'offset' to our hard-coded vertex position
gl_Position = vertices[gl_VertexID] + offset;
// Output a fixed value for vs_color
vs_out.color = color;
}
Listing 3.5: Vertex shader with an output interface block
Note that the interface block in Listing 3.5 has both a block name (VS_OUT, uppercase) and an instance name (vs_out, lowercase)(注意到Listing3.5的代码中同时出现了数据块的名字和它实例对象的名字). Interface blocks are matched between stages using the block name (VS_OUT in this case)(数据块在渲染管线中传递的时候是用名字一一对应的), but are referenced in shaders using the instance name(在使用的时候,却引用的是数据块实例的名字). Thus, modifying our fragment shader to use an interface block gives the code shown in Listing 3.6(所以,我们还要按照Listing3.6的那样修改咱们的fragment shader的代码).
#version 450 core // Declare VS_OUT as an input interface block in VS_OUT { vec4 color; // Send color to the next stage } fs_in; // Output to the framebuffer out vec4 color; void main(void) { // Simply assign the color we were given by the vertex shader to our output color = fs_in.color; }
Listing 3.6: Fragment shader with an input interface block
Matching interface blocks by block name but allowing block instances to have different names in each shader stage serves two important purposes(使用数据块的名字做一一对应,但允许数据块实例有不同的名字有俩目的). First, it allows the name by which you refer to the block to be different in each stage(第一个目的,它允许你在渲染管线引用的名字可以不一样), thereby avoiding confusing things such as having to use vs_out in a fragment shader(这样可以避免一些奇怪的东西,比如在fragment shader中使用vs_out这样的名字). Second, it allows interfaces to go from being single items to arrays when crossing between certain shader stages(第二,它允许传递数据的时候从一个数据变成一组数据), such as the vertex and tessellation or geometry shader stages(比如vertex shader的数据传递给tessellation或者geometry shader的时候,我们很快就会见识到了), as we will see in a short while. Note that interface blocks are only for moving data from shader stage to shader stage(注意,数据块只适用于shader各个阶段间传递数据)-you can't use them to group together inputs to the vertex shader or outputs from the fragment shader(你不能使用数据块对vertex shader传入数据或者从fragment shader输出数据).
本日的翻译就到这里,明天见,拜拜~~
第一时间获取最新桥段,请关注东汉书院以及图形之心公众号
东汉书院,等你来玩哦