How Does #version in GLSL Dictate Syntax and Features?
The #version directive is the mandatory first line in
OpenGL Shading Language (GLSL) source code that instructs the shader
compiler which language specification to enforce. By specifying a
version number and an optional profile, #version controls
the availability of modern shader stages, built-in variables, keywords,
data structures, and texture sampling functions. Omitting or
misconfiguring this directive forces compilers to default to legacy
specifications, fundamentally altering how inputs, outputs, and hardware
capabilities are parsed.
Core Mechanics and Syntax of the Directive
Every GLSL shader source must declare #version before
any other tokens or comments, except for whitespace and comments
depending on compiler tolerance. The general syntax follows:
#version number [profile]- Version Number: A three-digit integer matching the
GLSL version (such as
120,330,430, or460). From OpenGL 3.3 onward, GLSL version numbers mirror the OpenGL core specification versions directly (e.g., GLSL 4.50 corresponds to OpenGL 4.5). - Profile Identifier: Introduced in GLSL 1.50, the
profile flag specifies the context target:
core,compatibility, ores(for OpenGL ES and WebGL2).
If no profile is explicitly defined on desktop targets above GLSL
1.50, the compiler defaults to the core profile. If the
#version directive is entirely omitted from a shader, the
GLSL compiler falls back to version 110 (OpenGL 2.0),
triggering strict legacy parsing rules.
Evolution of Variable Qualifiers Across Versions
The declared #version drastically changes how data
enters and exits shader stages.
Legacy Qualifiers (GLSL 1.10 to 1.20)
In GLSL 1.10 and 1.20, data passing relies on fixed-function and stage-specific qualifiers:
attribute: Used exclusively in vertex shaders for per-vertex attributes.varying: Used to pass interpolated data from vertex shaders to fragment shaders.uniform: Shared constant data across all stages.- Built-in outputs such as
gl_FragColorandgl_Positionhandle direct pipeline communication.
Modern Unified Qualifiers (GLSL 1.30 and Above)
Starting with GLSL 1.30 (OpenGL 3.0), the language deprecated stage-specific data qualifiers in favor of unified in/out syntax:
inreplacesattributein vertex shaders and receives interpolated data in fragment shaders.outwrites data from vertex stages and defines user-declared render targets in fragment shaders.gl_FragColoris deprecated in 1.30 and completely removed in 3.30 core, requiring developers to declare explicitout vec4 fragColortargets.
Memory Layouts, Buffers, and Feature Unlocks
Modern GPU features are gated directly behind specific
#version targets:
- Uniform Buffer Objects (UBOs): Available starting
in GLSL 1.40/3.30 using the
uniform BlockName { ... };syntax to share large uniform blocks across programs. - Explicit Layout Qualifiers: GLSL 3.30 and 4.00
introduce
layout(location = 0)to bind vertex attribute indices and render target locations directly in shader code, removing the need for runtime API queries. - Shader Storage Buffer Objects (SSBOs) & Compute
Shaders: Declaring
#version 430unlocks the compute pipeline (layout(local_size_x = ...) in;) and read-writebufferblocks for massive data manipulation. - Bindless and SPIR-V Capabilities: Versions
450and460enable modern Vulkan-compatible semantics, enhanced memory qualifiers, and explicit binding models.
Impact of Profile Modifiers
The optional profile argument defines whether deprecated legacy functions remain accessible:
- Core Profile (
core): Strips all deprecated features, including fixed-function state variables (gl_ModelViewMatrix,gl_Vertex), legacy lighting states, and implicit texture functions. - Compatibility Profile (
compatibility): Retains full backward compatibility, allowing legacy variables (gl_FragColor,gl_Normal) to coexist with modern features. - Embedded Systems (
es): Enforces OpenGL ES specification constraints, mandatory precision qualifiers (precision highp float;), and mobile-optimized feature subsets required for mobile hardware and WebGL2 contexts.
Texture Sampling Function Overhauls
The #version declaration alters built-in sampling
functions. Prior to GLSL 1.30, sampling functions were explicitly named
after their texture types:
// GLSL 1.20
vec4 color = texture2D(u_Sampler, uv);
vec4 cube = textureCube(u_CubeMap, dir);In GLSL 1.30 and later, these overloaded functions were unified into a single polymorphic identifier:
// GLSL 330 core / 460 core
vec4 color = texture(u_Sampler, uv);
vec4 cube = texture(u_CubeMap, dir);Compiling overloaded texture() calls under
#version 120 results in a compilation failure, just as
invoking texture2D() in modern core profiles triggers
deprecation or unsupported symbol errors.