What Is the Difference Between in, out, and inout in GLSL?
In the OpenGL Shading Language (GLSL), function parameters use
storage qualifiers—specifically in, out, and
inout—to control how data flows between the caller and the
function body. Unlike languages like C++ that use pointers or memory
references, GLSL relies on value-copy semantics to pass data into and
out of functions. This guide explains the distinct behavior, lifecycle,
and use cases of each parameter qualifier so you can write predictable,
high-performance shader routines.
The Copy-In / Copy-Out Semantic Model
GLSL executes across highly parallel GPU hardware that typically does not support traditional memory address dereferencing or pointer arithmetic. To maintain performance and safety, GLSL functions use a copy-in / copy-out execution model:
- Copy-in: Values from the caller's arguments are copied into local parameter variables when the function begins.
- Copy-out: Values from the local parameter variables are copied back to the caller's arguments right before the function returns.
The qualifiers determine whether a parameter undergoes copy-in, copy-out, or both.
The
in Qualifier (Pass by Value / Read-Only Input)
The in qualifier signifies that a parameter is strictly
an input to the function. It is copied into the function scope at
invocation time.
- Default behavior: If you omit a qualifier entirely
(e.g.,
void compute(float val)), GLSL automatically treats it asin. - Caller safety: You can modify the variable inside the function body, but any changes remain local to that function and will not reflect in the caller's original variable.
- Requirements: The caller must pass an initialized value or expression (such as a constant or the result of another operation).
void computeBrightness(in vec3 color, in float factor) {
// Modifications to 'color' or 'factor' do not alter the caller's variables
color *= factor;
}The out
Qualifier (Write-Only Output)
The out qualifier defines a parameter used to return
data back to the calling scope. It undergoes only the copy-out step.
- Uninitialized upon entry: When the function begins,
the
outvariable has an undefined/uninitialized value within the function. You should not read from anoutparameter before writing to it. - Caller modification: Whatever value is written to the parameter during the function execution is written back to the caller's variable upon exit.
- Requirements: The caller must provide a writable l-value (e.g., an existing variable), not a literal constant or expression.
void getViewportBounds(out vec2 minBound, out vec2 maxBound) {
// Values are written and copied back to the caller upon return
minBound = vec2(0.0, 0.0);
maxBound = vec2(1920.0, 1080.0);
}The
inout Qualifier (Read-Write Input and Output)
The inout qualifier combines the behavior of both
in and out. It performs both the copy-in upon
entry and the copy-out upon exit.
- Read and write access: The parameter receives the initial value from the caller, can be read and mutated during function execution, and passes the updated value back to the caller when finished.
- Use case: Ideal for in-place modifications, iterative operations, stateful updates, or accumulator routines.
- Requirements: The caller must provide an initialized, writable l-value variable.
void applyVignette(inout vec3 color, in float radius) {
// Reads the incoming color value and updates it in-place
color *= smoothstep(0.8, 0.2, radius);
}Qualifier Comparison
| Qualifier | Direction | Initial Value on Entry | Written Back to Caller | Default Qualifier | Allowed Arguments |
|---|---|---|---|---|---|
in |
Caller \(\rightarrow\) Function | Value of caller's argument | No | Yes | Variables, constants, expressions |
out |
Function \(\rightarrow\) Caller | Undefined / Uninitialized | Yes | No | Writable variables (l-values) only |
inout |
Caller \(\leftrightarrow\) Function | Value of caller's argument | Yes | No | Writable variables (l-values) only |
Practical Code Example
The following shader function demonstrates all three qualifiers working together to process geometric attributes:
void transformPoint(
in mat4 modelMatrix, // Read-only input transformation
inout vec4 position, // Transformed in-place
out vec3 surfaceNormal // Computed and exported to caller
) {
// position is read (in) and modified (out)
position = modelMatrix * position;
// surfaceNormal is purely calculated and exported (out)
surfaceNormal = normalize(mat3(modelMatrix) * vec3(0.0, 1.0, 0.0));
}Understanding these qualifiers ensures correct data propagation across shader pipelines while preventing unexpected uninitialized reads or lost state updates during GPU execution.