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:

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.

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.

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.

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.