How Do GLSL In and Out Qualifiers Match Stages?
In the OpenGL Shading Language (GLSL), input and output qualifiers
(in and out) define the communication channels
between successive pipeline stages. These storage qualifiers establish
formal interfaces where data emitted by an upstream shader stage is
consumed by a downstream shader stage. Stage-to-stage matching relies on
strict rules of data types, naming conventions, interface blocks, and
layout location indexing to ensure seamless data flow across the
graphics pipeline.
The Role of in
and out Qualifiers
The programmable graphics pipeline operates as a sequence of discrete
processing stages, such as vertex, tessellation, geometry, and fragment
shaders. The out qualifier designates variables that export
data from the current stage, while the in qualifier
designates variables that import data from the preceding stage. During
program linking, the graphics driver validates that every active input
in a receiving stage has a compatible, corresponding output in the
producing stage.
Name and Type Matching Rules
Historically, GLSL matched interface variables primarily by name and type. When using standard matching rules:
- Name Matching: An output variable declared as
out vec3 vNormal;in the vertex shader automatically links to an input variable declared asin vec3 vNormal;in the fragment shader. - Type Compatibility: The variables must share the
same basic data type, component count, and precision qualifiers. A
mismatch, such as sending a
vec4to an input expecting avec3, leads to compilation or link-time errors. - Interpolation Qualifiers: Auxiliary qualifiers such
as
flat,smooth, ornoperspectivemust match identically between the output and input declarations to ensure consistent rasterization behavior.
Explicit Location Matching with Layout Qualifiers
Modern GLSL workflows commonly use the
layout(location = n) qualifier to decouple variable names
from stage linking. By assigning an explicit numerical index to an
output and input pair, the pipeline matches the interface based entirely
on the slot index rather than identifier names:
// Vertex Shader Output
layout(location = 0) out vec2 texCoord;
// Fragment Shader Input
layout(location = 0) in vec2 uv;Explicit location matching prevents naming collisions, simplifies shader code refactoring, and is mandatory in modern graphics APIs such as Vulkan. It also allows separate shader objects to be linked dynamically without requiring variable name coordination.
Uniform Interface Blocks across Multi-Vertex Stages
Certain stages change the primitive topology and require arrayed inputs. For example, a geometry shader processes an entire primitive (such as a triangle with three vertices) rather than a single vertex. In these scenarios, outputs from the vertex shader match into arrayed inputs or interface blocks in the geometry shader:
// Vertex Shader
out VS_OUT {
vec3 normal;
vec2 uv;
} vs_out;
// Geometry Shader
in VS_OUT {
vec3 normal;
vec2 uv;
} gs_in[];Interface blocks group related variables into a single semantic unit, ensuring that complex data layouts remain structured, type-safe, and cleanly mapped across distinct execution granularities.