Input and output interface blocks were designed for the expressed purpose of solving this problem. This is why interface blocks have instance names. Consider:
//Vertex Shader
in vec2 TexCoords;
out VertexData
{
vec2 TexCoords;
} vs_out;
void main()
{
//...
vs_out.TexCoords = TexCoords;
//...
}
Notice how the output interface block VertexData specifies an instance name (vs_out
). This means that all access to variables within the interface block must be qualified by the instance name. Because this removes [var]TexCoords[/var] from the global namespace, it is OK to declare a global input variable with the same name. GLSL knows which one you’re talking about.
If you had not used an interface name, then the compiler would complain, as the variable in the output interface block would have the same name as the non-block input variable.
//Fragment Shader
in VertexData
{
vec2 TexCoords;
} fs_in;
void main()
{
//...
vec4 color = texture(tex, fs_in.TexCoords);
//...
}
The fragment shader’s input interface block VertexData matches the vertex shader’s output interface block VertexData. Note that the fragment shader uses the same block name, but a different instance name. That’s perfectly valid. Indeed, it could use no instance name at all and still be fine.
Now here’s where the magic happens. The following geometry shader can slip between the vertex and fragment shaders without any modification to them:
//Geometry Shader
in VertexData
{
vec2 TexCoords;
} gs_in[];
out VertexData
{
vec2 TexCoords;
} gs_out;
void main()
{
//...
gs_out.TexCoords = gs_in[INDEX].TexCoords;
}
You saw before that the interface block’s instance name allows two variables to have the same basename, yet still be differentiated due to qualifying them with the instance name. Here, the same thing applies, just between two separate interface blocks.
And yes, even though the two interface blocks have the same block name, they are separate. Why? Because one is an input interface block, and the other is an output interface block. Now, you still need difference instance names there, so that the shader code can differentiate the two variables. But that’s all you need.
The input interface block VertexData matches the output interface block VertexData in the VS. While the output interface block VertexData matches the input interface block VertexData in the FS.
And everyone’s happy.
Alternatively, you can simply not link your shaders together in the same program. The above code with interface blocks would work just fine. But using separate programs also allows for direct linking by integers rather than names, so that different stages could name the variables different things.